chiark / gitweb /
systemd-python: small cleanups
[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 <stdio.h>
26
27 #include <systemd/sd-journal.h>
28
29 #include "pyutil.h"
30 #include "macro.h"
31 #include "util.h"
32
33 typedef struct {
34     PyObject_HEAD
35     sd_journal *j;
36 } Reader;
37 static PyTypeObject ReaderType;
38
39 static int set_error(int r, const char* path, const char* invalid_message) {
40     if (r >= 0)
41         return r;
42     if (r == -EINVAL && invalid_message)
43         PyErr_SetString(PyExc_ValueError, invalid_message);
44     else if (r == -ENOMEM)
45         PyErr_SetString(PyExc_MemoryError, "Not enough memory");
46     else {
47         errno = -r;
48         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
49     }
50     return -1;
51 }
52
53
54 PyDoc_STRVAR(module__doc__,
55              "Class to reads the systemd journal similar to journalctl.");
56
57
58 #if PY_MAJOR_VERSION >= 3
59 static PyTypeObject MonotonicType;
60
61 PyDoc_STRVAR(MonotonicType__doc__,
62              "A tuple of (timestamp, bootid) for holding monotonic timestamps");
63
64 static PyStructSequence_Field MonotonicType_fields[] = {
65     {(char*) "timestamp", (char*) "Time"},
66     {(char*) "bootid", (char*) "Unique identifier of the boot"},
67     {NULL, NULL}
68 };
69
70 static PyStructSequence_Desc Monotonic_desc = {
71     (char*) "journal.Monotonic",
72     MonotonicType__doc__,
73     MonotonicType_fields,
74     2,
75 };
76 #endif
77
78
79 static void Reader_dealloc(Reader* self)
80 {
81     sd_journal_close(self->j);
82     Py_TYPE(self)->tp_free((PyObject*)self);
83 }
84
85 PyDoc_STRVAR(Reader__doc__,
86              "Reader([flags | path]) -> ...\n\n"
87              "Reader allows filtering and retrieval of Journal entries.\n"
88              "Note: this is a low-level interface, and probably not what you\n"
89              "want, use systemd.journal.Reader instead.\n\n"
90              "Argument `flags` sets open flags of the journal, which can be one\n"
91              "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
92              "journal on local machine only; RUNTIME_ONLY opens only\n"
93              "volatile journal files; and SYSTEM_ONLY opens only\n"
94              "journal files of system services and the kernel.\n\n"
95              "Argument `path` is the directory of journal files. Note that\n"
96              "`flags` and `path` are exclusive.\n");
97 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
98 {
99     int flags = 0, r;
100     char *path = NULL;
101
102     static const char* const kwlist[] = {"flags", "path", NULL};
103     if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
104                                      &flags, &path))
105         return -1;
106
107     if (!flags)
108         flags = SD_JOURNAL_LOCAL_ONLY;
109     else
110         if (path) {
111             PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
112             return -1;
113         }
114
115     Py_BEGIN_ALLOW_THREADS
116     if (path)
117         r = sd_journal_open_directory(&self->j, path, 0);
118     else
119         r = sd_journal_open(&self->j, flags);
120     Py_END_ALLOW_THREADS
121
122     return set_error(r, path, "Invalid flags or path");
123 }
124
125
126 PyDoc_STRVAR(Reader_fileno__doc__,
127              "fileno() -> int\n\n"
128              "Get a file descriptor to poll for changes in the journal.\n"
129              "This method invokes sd_journal_get_fd().\n"
130              "See man:sd_journal_get_fd(3).");
131 static PyObject* Reader_fileno(Reader *self, PyObject *args)
132 {
133     int r;
134     r = sd_journal_get_fd(self->j);
135     set_error(r, NULL, NULL);
136     if (r < 0)
137         return NULL;
138     return long_FromLong(r);
139 }
140
141
142 PyDoc_STRVAR(Reader_reliable_fd__doc__,
143              "reliable_fd() -> bool\n\n"
144              "Returns True iff the journal can be polled reliably.\n"
145              "This method invokes sd_journal_reliable_fd().\n"
146              "See man:sd_journal_reliable_fd(3).");
147 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
148 {
149     int r;
150     r = sd_journal_reliable_fd(self->j);
151     set_error(r, NULL, NULL);
152     if (r < 0)
153         return NULL;
154     return PyBool_FromLong(r);
155 }
156
157
158 PyDoc_STRVAR(Reader_close__doc__,
159              "close() -> None\n\n"
160              "Free resources allocated by this Reader object.\n"
161              "This method invokes sd_journal_close().\n"
162              "See man:sd_journal_close(3).");
163 static PyObject* Reader_close(Reader *self, PyObject *args)
164 {
165     assert(self);
166     assert(!args);
167
168     sd_journal_close(self->j);
169     self->j = NULL;
170     Py_RETURN_NONE;
171 }
172
173
174 PyDoc_STRVAR(Reader___enter____doc__,
175              "__enter__() -> self\n\n"
176              "Part of the context manager protocol.\n"
177              "Returns self.\n");
178 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
179 {
180     assert(self);
181     assert(!args);
182
183     Py_INCREF(self);
184     return self;
185 }
186
187 PyDoc_STRVAR(Reader___exit____doc__,
188              "__exit__(type, value, traceback) -> None\n\n"
189              "Part of the context manager protocol.\n"
190              "Closes the journal.\n");
191 static PyObject* Reader___exit__(Reader *self, PyObject *args)
192 {
193     assert(self);
194
195     sd_journal_close(self->j);
196     self->j = NULL;
197     Py_RETURN_NONE;
198 }
199
200
201 PyDoc_STRVAR(Reader_get_next__doc__,
202              "get_next([skip]) -> dict\n\n"
203              "Return dictionary of the next log entry. Optional skip value will\n"
204              "return the `skip`\\-th log entry.");
205 static PyObject* Reader_get_next(Reader *self, PyObject *args)
206 {
207     PyObject *dict;
208     const void *msg;
209     size_t msg_len;
210     int64_t skip = 1LL;
211     int r;
212
213     if (!PyArg_ParseTuple(args, "|L:_Reader.get_next", &skip))
214         return NULL;
215
216     if (skip == 0LL) {
217         PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
218         return NULL;
219     }
220
221     Py_BEGIN_ALLOW_THREADS
222     if (skip == 1LL)
223         r = sd_journal_next(self->j);
224     else if (skip == -1LL)
225         r = sd_journal_previous(self->j);
226     else if (skip > 1LL)
227         r = sd_journal_next_skip(self->j, skip);
228     else if (skip < -1LL)
229         r = sd_journal_previous_skip(self->j, -skip);
230     else
231         assert_not_reached("should not be here");
232     Py_END_ALLOW_THREADS
233
234     set_error(r, NULL, NULL);
235     if (r < 0)
236         return NULL;
237     else if (r == 0) /* EOF */
238         return PyDict_New();
239
240     dict = PyDict_New();
241     if (!dict)
242             return NULL;
243
244     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
245         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
246         const char *delim_ptr;
247
248         delim_ptr = memchr(msg, '=', msg_len);
249         if (!delim_ptr) {
250             PyErr_SetString(PyExc_OSError,
251                             "journal gave us a field without '='");
252             goto error;
253         }
254
255         key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
256         if (!key)
257             goto error;
258
259         value = PyBytes_FromStringAndSize(
260                 delim_ptr + 1,
261                 (const char*) msg + msg_len - (delim_ptr + 1) );
262         if (!value)
263             goto error;
264
265         if (PyDict_Contains(dict, key)) {
266             PyObject *cur_value = PyDict_GetItem(dict, key);
267
268             if (PyList_CheckExact(cur_value)) {
269                 r = PyList_Append(cur_value, value);
270                 if (r < 0)
271                     goto error;
272             } else {
273                 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
274                 if (!tmp_list)
275                     goto error;
276
277                 r = PyList_Append(tmp_list, cur_value);
278                 if (r < 0)
279                     goto error;
280
281                 r = PyList_Append(tmp_list, value);
282                 if (r < 0)
283                     goto error;
284
285                 r = PyDict_SetItem(dict, key, tmp_list);
286                 if (r < 0)
287                     goto error;
288             }
289         } else {
290             r = PyDict_SetItem(dict, key, value);
291             if (r < 0)
292                 goto error;
293         }
294     }
295
296     {
297         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
298         uint64_t realtime;
299
300         r = sd_journal_get_realtime_usec(self->j, &realtime);
301         if (set_error(r, NULL, NULL))
302             goto error;
303
304         key = unicode_FromString("__REALTIME_TIMESTAMP");
305         if (!key)
306             goto error;
307
308         assert_cc(sizeof(unsigned long long) == sizeof(realtime));
309         value = PyLong_FromUnsignedLongLong(realtime);
310         if (!value)
311             goto error;
312
313         if (PyDict_SetItem(dict, key, value))
314             goto error;
315     }
316
317     {
318         PyObject _cleanup_Py_DECREF_
319             *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL;
320         sd_id128_t id;
321         uint64_t monotonic;
322
323         r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id);
324         if (set_error(r, NULL, NULL))
325             goto error;
326
327         assert_cc(sizeof(unsigned long long) == sizeof(monotonic));
328         key = unicode_FromString("__MONOTONIC_TIMESTAMP");
329         timestamp = PyLong_FromUnsignedLongLong(monotonic);
330         bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
331 #if PY_MAJOR_VERSION >= 3
332         value = PyStructSequence_New(&MonotonicType);
333 #else
334         value = PyTuple_New(2);
335 #endif
336         if (!key || !timestamp || !bytes || !value)
337             goto error;
338
339         Py_INCREF(timestamp);
340         Py_INCREF(bytes);
341
342 #if PY_MAJOR_VERSION >= 3
343         PyStructSequence_SET_ITEM(value, 0, timestamp);
344         PyStructSequence_SET_ITEM(value, 1, bytes);
345 #else
346         PyTuple_SET_ITEM(value, 0, timestamp);
347         PyTuple_SET_ITEM(value, 1, bytes);
348 #endif
349
350         if (PyDict_SetItem(dict, key, value))
351             goto error;
352     }
353
354     {
355         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
356         char _cleanup_free_ *cursor = NULL;
357
358         r = sd_journal_get_cursor(self->j, &cursor);
359         if (set_error(r, NULL, NULL))
360             goto error;
361
362         key = unicode_FromString("__CURSOR");
363         if (!key)
364             goto error;
365
366         value = PyBytes_FromString(cursor);
367         if (!value)
368             goto error;
369
370         if (PyDict_SetItem(dict, key, value))
371             goto error;
372     }
373
374     return dict;
375 error:
376     Py_DECREF(dict);
377     return NULL;
378 }
379
380
381 PyDoc_STRVAR(Reader_get_previous__doc__,
382              "get_previous([skip]) -> dict\n\n"
383              "Return dictionary of the previous log entry. Optional skip value\n"
384              "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
385 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
386 {
387     int64_t skip = 1LL;
388     if (!PyArg_ParseTuple(args, "|L:_Reader.get_previous", &skip))
389         return NULL;
390
391     return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
392                                (char*) "L", -skip);
393 }
394
395
396 PyDoc_STRVAR(Reader_add_match__doc__,
397              "add_match(match) -> None\n\n"
398              "Add a match to filter journal log entries. All matches of different\n"
399              "fields are combined with logical AND, and matches of the same field\n"
400              "are automatically combined with logical OR.\n"
401              "Match is a string of the form \"FIELD=value\".");
402 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
403 {
404     char *match;
405     int match_len, r;
406     if (!PyArg_ParseTuple(args, "s#:_Reader.add_match", &match, &match_len))
407         return NULL;
408
409     r = sd_journal_add_match(self->j, match, match_len);
410     set_error(r, NULL, "Invalid match");
411     if (r < 0)
412             return NULL;
413
414     Py_RETURN_NONE;
415 }
416
417
418 PyDoc_STRVAR(Reader_add_disjunction__doc__,
419              "add_disjunction() -> None\n\n"
420              "Inserts a logical OR between matches added before and afterwards.");
421 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
422 {
423     int r;
424     r = sd_journal_add_disjunction(self->j);
425     set_error(r, NULL, NULL);
426     if (r < 0)
427         return NULL;
428     Py_RETURN_NONE;
429 }
430
431
432 PyDoc_STRVAR(Reader_flush_matches__doc__,
433              "flush_matches() -> None\n\n"
434              "Clear all current match filters.");
435 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
436 {
437     sd_journal_flush_matches(self->j);
438     Py_RETURN_NONE;
439 }
440
441
442 PyDoc_STRVAR(Reader_seek_head__doc__,
443              "seek_head() -> None\n\n"
444              "Jump to the beginning of the journal.\n"
445              "This method invokes sd_journal_seek_head().\n"
446              "See man:sd_journal_seek_head(3).");
447 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
448 {
449     int r;
450     Py_BEGIN_ALLOW_THREADS
451     r = sd_journal_seek_head(self->j);
452     Py_END_ALLOW_THREADS
453     if (set_error(r, NULL, NULL))
454         return NULL;
455     Py_RETURN_NONE;
456 }
457
458
459 PyDoc_STRVAR(Reader_seek_tail__doc__,
460              "seek_tail() -> None\n\n"
461              "Jump to the end of the journal.\n"
462              "This method invokes sd_journal_seek_tail().\n"
463              "See man:sd_journal_seek_tail(3).");
464 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
465 {
466     int r;
467     Py_BEGIN_ALLOW_THREADS
468     r = sd_journal_seek_tail(self->j);
469     Py_END_ALLOW_THREADS
470     if (set_error(r, NULL, NULL))
471         return NULL;
472     Py_RETURN_NONE;
473 }
474
475
476 PyDoc_STRVAR(Reader_seek_realtime__doc__,
477              "seek_realtime(realtime) -> None\n\n"
478              "Seek to nearest matching journal entry to `realtime`. Argument\n"
479              "`realtime` can must be an integer unix timestamp.");
480 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
481 {
482     double timedouble;
483     uint64_t timestamp;
484     int r;
485
486     if (!PyArg_ParseTuple(args, "d:_Reader.seek_realtime", &timedouble))
487         return NULL;
488
489     timestamp = (uint64_t) (timedouble * 1.0E6);
490     if ((int64_t) timestamp < 0LL) {
491         PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
492         return NULL;
493     }
494
495     Py_BEGIN_ALLOW_THREADS
496     r = sd_journal_seek_realtime_usec(self->j, timestamp);
497     Py_END_ALLOW_THREADS
498     if (set_error(r, NULL, NULL))
499         return NULL;
500     Py_RETURN_NONE;
501 }
502
503
504 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
505              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
506              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
507              "`monotonic` is an timestamp from boot in seconds.\n"
508              "Argument `bootid` is a string representing which boot the\n"
509              "monotonic time is reference to. Defaults to current bootid.");
510 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
511 {
512     double timedouble;
513     char *bootid = NULL;
514     uint64_t timestamp;
515     sd_id128_t id;
516     int r;
517
518     if (!PyArg_ParseTuple(args, "d|z:_Reader.seek_monotonic", &timedouble, &bootid))
519         return NULL;
520
521     timestamp = (uint64_t) (timedouble * 1.0E6);
522
523     if ((int64_t) timestamp < 0LL) {
524         PyErr_SetString(PyExc_ValueError, "Time must be positive number");
525         return NULL;
526     }
527
528     if (bootid) {
529         r = sd_id128_from_string(bootid, &id);
530         if (set_error(r, NULL, "Invalid bootid"))
531             return NULL;
532     } else {
533         Py_BEGIN_ALLOW_THREADS
534         r = sd_id128_get_boot(&id);
535         Py_END_ALLOW_THREADS
536         if (set_error(r, NULL, NULL))
537             return NULL;
538     }
539
540     Py_BEGIN_ALLOW_THREADS
541     r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
542     Py_END_ALLOW_THREADS
543     if (set_error(r, NULL, NULL))
544         return NULL;
545     Py_RETURN_NONE;
546 }
547
548
549 PyDoc_STRVAR(Reader_wait__doc__,
550              "wait([timeout]) -> state change (integer)\n\n"
551              "Wait for a change in the journal. Argument `timeout` specifies\n"
552              "the maximum number of seconds to wait before returning\n"
553              "regardless of wheter the journal has changed. If `timeout` is not given\n"
554              "or is 0, then block forever.\n"
555              "Will return constants: NOP if no change; APPEND if new\n"
556              "entries have been added to the end of the journal; and\n"
557              "INVALIDATE if journal files have been added or removed.");
558 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
559 {
560     int r;
561     int64_t timeout = 0LL;
562
563     if (!PyArg_ParseTuple(args, "|L:_Reader.wait", &timeout))
564         return NULL;
565
566     Py_BEGIN_ALLOW_THREADS
567     r = sd_journal_wait(self->j,
568                         timeout == 0 ? (uint64_t) -1 : timeout * 1E6);
569     Py_END_ALLOW_THREADS
570     if (set_error(r, NULL, NULL) < 0)
571         return NULL;
572
573     return long_FromLong(r);
574 }
575
576
577 PyDoc_STRVAR(Reader_seek_cursor__doc__,
578              "seek_cursor(cursor) -> None\n\n"
579              "Seek to journal entry by given unique reference `cursor`.");
580 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
581 {
582     const char *cursor;
583     int r;
584
585     if (!PyArg_ParseTuple(args, "s:_Reader.seek_cursor", &cursor))
586         return NULL;
587
588     Py_BEGIN_ALLOW_THREADS
589     r = sd_journal_seek_cursor(self->j, cursor);
590     Py_END_ALLOW_THREADS
591     if (set_error(r, NULL, "Invalid cursor"))
592         return NULL;
593     Py_RETURN_NONE;
594 }
595
596
597 static PyObject* Reader_iter(PyObject *self)
598 {
599     Py_INCREF(self);
600     return self;
601 }
602
603 static PyObject* Reader_iternext(PyObject *self)
604 {
605     PyObject *dict;
606     Py_ssize_t dict_size;
607
608     dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
609     if (PyErr_Occurred())
610         return NULL;
611     dict_size = PyDict_Size(dict);
612     if ((int64_t) dict_size > 0LL) {
613         return dict;
614     } else {
615         Py_DECREF(dict);
616         PyErr_SetNone(PyExc_StopIteration);
617         return NULL;
618     }
619 }
620
621
622 PyDoc_STRVAR(Reader_query_unique__doc__,
623              "query_unique(field) -> a set of values\n\n"
624              "Return a set of unique values appearing in journal for the\n"
625              "given `field`. Note this does not respect any journal matches.");
626 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
627 {
628     char *query;
629     int r;
630     const void *uniq;
631     size_t uniq_len;
632     PyObject *value_set, *key, *value;
633
634     if (!PyArg_ParseTuple(args, "s:_Reader.query_unique", &query))
635         return NULL;
636
637     Py_BEGIN_ALLOW_THREADS
638     r = sd_journal_query_unique(self->j, query);
639     Py_END_ALLOW_THREADS
640     if (set_error(r, NULL, "Invalid field name"))
641         return NULL;
642
643     value_set = PySet_New(0);
644     key = unicode_FromString(query);
645
646     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
647         const char *delim_ptr;
648
649         delim_ptr = memchr(uniq, '=', uniq_len);
650         value = PyBytes_FromStringAndSize(
651             delim_ptr + 1,
652             (const char*) uniq + uniq_len - (delim_ptr + 1));
653         PySet_Add(value_set, value);
654         Py_DECREF(value);
655     }
656     Py_DECREF(key);
657     return value_set;
658 }
659
660
661 PyDoc_STRVAR(Reader_get_catalog__doc__,
662              "get_catalog() -> str\n\n"
663              "Retrieve a message catalog entry for the current journal entry.\n"
664              "Wraps man:sd_journal_get_catalog(3).");
665 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
666 {
667     int r;
668     char _cleanup_free_ *msg = NULL;
669
670     assert(self);
671     assert(!args);
672
673     Py_BEGIN_ALLOW_THREADS
674     r = sd_journal_get_catalog(self->j, &msg);
675     Py_END_ALLOW_THREADS
676     if (set_error(r, NULL, NULL))
677         return NULL;
678
679     return unicode_FromString(msg);
680 }
681
682
683 PyDoc_STRVAR(get_catalog__doc__,
684              "get_catalog(id128) -> str\n\n"
685              "Retrieve a message catalog entry for the given id.\n"
686              "Wraps man:sd_journal_get_catalog_for_message_id(3).");
687 static PyObject* get_catalog(PyObject *self, PyObject *args)
688 {
689     int r;
690     char *id_ = NULL;
691     sd_id128_t id;
692     char _cleanup_free_ *msg = NULL;
693
694     assert(!self);
695     assert(args);
696
697     if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
698         return NULL;
699
700     r = sd_id128_from_string(id_, &id);
701     if (set_error(r, NULL, "Invalid id128"))
702         return NULL;
703
704     Py_BEGIN_ALLOW_THREADS
705     r = sd_journal_get_catalog_for_message_id(id, &msg);
706     Py_END_ALLOW_THREADS
707     if (set_error(r, NULL, NULL))
708         return NULL;
709
710     return unicode_FromString(msg);
711 }
712
713
714 PyDoc_STRVAR(data_threshold__doc__,
715              "Threshold for field size truncation in bytes.\n\n"
716              "Fields longer than this will be truncated to the threshold size.\n"
717              "Defaults to 64Kb.");
718
719 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
720 {
721     size_t cvalue;
722     int r;
723
724     r = sd_journal_get_data_threshold(self->j, &cvalue);
725     if (set_error(r, NULL, NULL))
726         return NULL;
727
728     return long_FromSize_t(cvalue);
729 }
730
731 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
732 {
733     int r;
734     if (value == NULL) {
735         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
736         return -1;
737     }
738     if (!long_Check(value)){
739         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
740         return -1;
741     }
742     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
743     return set_error(r, NULL, NULL);
744 }
745
746
747 PyDoc_STRVAR(closed__doc__,
748              "True iff journal is closed");
749 static PyObject* Reader_get_closed(Reader *self, void *closure)
750 {
751     return PyBool_FromLong(self->j == NULL);
752 }
753
754
755 static PyGetSetDef Reader_getsetters[] = {
756     {(char*) "data_threshold",
757      (getter) Reader_get_data_threshold,
758      (setter) Reader_set_data_threshold,
759      (char*) data_threshold__doc__,
760      NULL},
761     {(char*) "closed",
762      (getter) Reader_get_closed,
763      NULL,
764      (char*) closed__doc__,
765      NULL},
766     {NULL}
767 };
768
769 static PyMethodDef Reader_methods[] = {
770     {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
771     {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
772     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
773     {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
774     {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
775     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
776     {"get_next",        (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
777     {"get_previous",    (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
778     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
779     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
780     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
781     {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
782     {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
783     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
784     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
785     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
786     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
787     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
788     {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
789     {NULL}  /* Sentinel */
790 };
791
792 static PyTypeObject ReaderType = {
793     PyVarObject_HEAD_INIT(NULL, 0)
794     "_reader._Reader",                        /*tp_name*/
795     sizeof(Reader),                           /*tp_basicsize*/
796     0,                                        /*tp_itemsize*/
797     (destructor)Reader_dealloc,               /*tp_dealloc*/
798     0,                                        /*tp_print*/
799     0,                                        /*tp_getattr*/
800     0,                                        /*tp_setattr*/
801     0,                                        /*tp_compare*/
802     0,                                        /*tp_repr*/
803     0,                                        /*tp_as_number*/
804     0,                                        /*tp_as_sequence*/
805     0,                                        /*tp_as_mapping*/
806     0,                                        /*tp_hash */
807     0,                                        /*tp_call*/
808     0,                                        /*tp_str*/
809     0,                                        /*tp_getattro*/
810     0,                                        /*tp_setattro*/
811     0,                                        /*tp_as_buffer*/
812     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
813     Reader__doc__,                            /* tp_doc */
814     0,                                        /* tp_traverse */
815     0,                                        /* tp_clear */
816     0,                                        /* tp_richcompare */
817     0,                                        /* tp_weaklistoffset */
818     Reader_iter,                              /* tp_iter */
819     Reader_iternext,                          /* tp_iternext */
820     Reader_methods,                           /* tp_methods */
821     0,                                        /* tp_members */
822     Reader_getsetters,                        /* tp_getset */
823     0,                                        /* tp_base */
824     0,                                        /* tp_dict */
825     0,                                        /* tp_descr_get */
826     0,                                        /* tp_descr_set */
827     0,                                        /* tp_dictoffset */
828     (initproc) Reader_init,                   /* tp_init */
829     0,                                        /* tp_alloc */
830     PyType_GenericNew,                        /* tp_new */
831 };
832
833 static PyMethodDef methods[] = {
834         { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
835         { NULL, NULL, 0, NULL }        /* Sentinel */
836 };
837
838 #if PY_MAJOR_VERSION >= 3
839 static PyModuleDef module = {
840     PyModuleDef_HEAD_INIT,
841     "_reader",
842     module__doc__,
843     -1,
844     methods,
845     NULL, NULL, NULL, NULL
846 };
847 #endif
848
849 #if PY_MAJOR_VERSION >= 3
850 static bool initialized = false;
851 #endif
852
853 #pragma GCC diagnostic push
854 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
855
856 PyMODINIT_FUNC
857 #if PY_MAJOR_VERSION >= 3
858 PyInit__reader(void)
859 #else
860 init_reader(void)
861 #endif
862 {
863     PyObject* m;
864
865     PyDateTime_IMPORT;
866
867     if (PyType_Ready(&ReaderType) < 0)
868 #if PY_MAJOR_VERSION >= 3
869         return NULL;
870 #else
871         return;
872 #endif
873
874 #if PY_MAJOR_VERSION >= 3
875     m = PyModule_Create(&module);
876     if (m == NULL)
877         return NULL;
878
879     if (!initialized) {
880         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
881         initialized = true;
882     }
883 #else
884     m = Py_InitModule3("_reader", methods, module__doc__);
885     if (m == NULL)
886         return;
887 #endif
888
889     Py_INCREF(&ReaderType);
890 #if PY_MAJOR_VERSION >= 3
891     Py_INCREF(&MonotonicType);
892 #endif
893     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
894 #if PY_MAJOR_VERSION >= 3
895         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
896 #endif
897         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
898         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
899         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
900         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
901         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
902         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
903 #if PY_MAJOR_VERSION >= 3
904         Py_DECREF(m);
905         return NULL;
906 #endif
907     }
908
909 #if PY_MAJOR_VERSION >= 3
910     return m;
911 #endif
912 }
913
914 #pragma GCC diagnostic pop