chiark / gitweb /
systemd-python: updated _reader header to standard license
[elogind.git] / src / python-systemd / _reader.c
1 /*-*- Mode: C; c-basic-offset: 8; 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 #include <systemd/sd-journal.h>
22
23 #include <Python.h>
24 #include <structmember.h>
25 #include <datetime.h>
26
27 typedef struct {
28     PyObject_HEAD
29     sd_journal *j;
30 } Journal;
31 static PyTypeObject JournalType;
32
33 static void
34 Journal_dealloc(Journal* self)
35 {
36     sd_journal_close(self->j);
37     Py_TYPE(self)->tp_free((PyObject*)self);
38 }
39
40 PyDoc_STRVAR(Journal__doc__,
41 "Journal([flags][,path]) -> ...\n"
42 "Journal instance\n\n"
43 "Returns instance of Journal, which allows filtering and return\n"
44 "of journal entries.\n"
45 "Argument `flags` sets open flags of the journal, which can be one\n"
46 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
47 "journal on local machine only; RUNTIME_ONLY opens only\n"
48 "volatile journal files; and SYSTEM_ONLY opens only\n"
49 "journal files of system services and the kernel.\n"
50 "Argument `path` is the directory of journal files. Note that\n"
51 "currently flags are ignored when `path` is present as they are\n"
52 " not relevant.");
53 static int
54 Journal_init(Journal *self, PyObject *args, PyObject *keywds)
55 {
56     int flags=SD_JOURNAL_LOCAL_ONLY;
57     char *path=NULL;
58
59     static char *kwlist[] = {"flags", "path", NULL};
60     if (! PyArg_ParseTupleAndKeywords(args, keywds, "|is", kwlist,
61                                       &flags, &path))
62         return 1;
63
64     int r;
65     Py_BEGIN_ALLOW_THREADS
66     if (path) {
67         r = sd_journal_open_directory(&self->j, path, 0);
68     }else{
69         r = sd_journal_open(&self->j, flags);
70     }
71     Py_END_ALLOW_THREADS
72     if (r < 0) {
73         errno = -r;
74         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
75                             r == -ENOMEM ? PyExc_MemoryError :
76                             PyExc_OSError;
77         PyErr_SetFromErrnoWithFilename(errtype, path);
78         return -1;
79     }
80
81     return 0;
82 }
83
84 PyDoc_STRVAR(Journal_get_next__doc__,
85 "get_next([skip]) -> dict\n\n"
86 "Return dictionary of the next log entry. Optional skip value will\n"
87 "return the `skip`th log entry.");
88 static PyObject *
89 Journal_get_next(Journal *self, PyObject *args)
90 {
91     int64_t skip=1LL;
92     if (! PyArg_ParseTuple(args, "|L", &skip))
93         return NULL;
94
95     if (skip == 0LL) {
96         PyErr_SetString(PyExc_ValueError, "Skip number must positive/negative integer");
97         return NULL;
98     }
99
100     int r;
101     Py_BEGIN_ALLOW_THREADS
102     if (skip == 1LL) {
103         r = sd_journal_next(self->j);
104     }else if (skip == -1LL) {
105         r = sd_journal_previous(self->j);
106     }else if (skip > 1LL) {
107         r = sd_journal_next_skip(self->j, skip);
108     }else if (skip < -1LL) {
109         r = sd_journal_previous_skip(self->j, -skip);
110     }
111     Py_END_ALLOW_THREADS
112
113     if (r < 0) {
114         errno = -r;
115         PyErr_SetFromErrno(PyExc_OSError);
116         return NULL;
117     }else if ( r == 0) { //EOF
118         return PyDict_New();
119     }
120
121     PyObject *dict;
122     dict = PyDict_New();
123
124     const void *msg;
125     size_t msg_len;
126     const char *delim_ptr;
127     PyObject *key, *value, *cur_value, *tmp_list;
128
129     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
130         delim_ptr = memchr(msg, '=', msg_len);
131 #if PY_MAJOR_VERSION >=3
132         key = PyUnicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
133 #else
134         key = PyString_FromStringAndSize(msg, delim_ptr - (const char*) msg);
135 #endif
136         value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) msg + msg_len - (delim_ptr + 1) );
137         if (PyDict_Contains(dict, key)) {
138             cur_value = PyDict_GetItem(dict, key);
139             if (PyList_CheckExact(cur_value)) {
140                 PyList_Append(cur_value, value);
141             }else{
142                 tmp_list = PyList_New(0);
143                 PyList_Append(tmp_list, cur_value);
144                 PyList_Append(tmp_list, value);
145                 PyDict_SetItem(dict, key, tmp_list);
146                 Py_DECREF(tmp_list);
147             }
148         }else{
149             PyDict_SetItem(dict, key, value);
150         }
151         Py_DECREF(key);
152         Py_DECREF(value);
153     }
154
155     uint64_t realtime;
156     if (sd_journal_get_realtime_usec(self->j, &realtime) == 0) {
157         char realtime_str[20];
158         sprintf(realtime_str, "%llu", (long long unsigned) realtime);
159
160 #if PY_MAJOR_VERSION >=3
161         key = PyUnicode_FromString("__REALTIME_TIMESTAMP");
162 #else
163         key = PyString_FromString("__REALTIME_TIMESTAMP");
164 #endif
165         value = PyBytes_FromString(realtime_str);
166         PyDict_SetItem(dict, key, value);
167         Py_DECREF(key);
168         Py_DECREF(value);
169     }
170
171     sd_id128_t sd_id;
172     uint64_t monotonic;
173     if (sd_journal_get_monotonic_usec(self->j, &monotonic, &sd_id) == 0) {
174         char monotonic_str[20];
175         sprintf(monotonic_str, "%llu", (long long unsigned) monotonic);
176 #if PY_MAJOR_VERSION >=3
177         key = PyUnicode_FromString("__MONOTONIC_TIMESTAMP");
178 #else
179         key = PyString_FromString("__MONOTONIC_TIMESTAMP");
180 #endif
181         value = PyBytes_FromString(monotonic_str);
182
183         PyDict_SetItem(dict, key, value);
184         Py_DECREF(key);
185         Py_DECREF(value);
186     }
187
188     char *cursor;
189     if (sd_journal_get_cursor(self->j, &cursor) > 0) { //Should return 0...
190 #if PY_MAJOR_VERSION >=3
191         key = PyUnicode_FromString("__CURSOR");
192 #else
193         key = PyString_FromString("__CURSOR");
194 #endif
195         value = PyBytes_FromString(cursor);
196         PyDict_SetItem(dict, key, value);
197         free(cursor);
198         Py_DECREF(key);
199         Py_DECREF(value);
200     }
201
202     return dict;
203 }
204
205 PyDoc_STRVAR(Journal_get_previous__doc__,
206 "get_previous([skip]) -> dict\n\n"
207 "Return dictionary of the previous log entry. Optional skip value\n"
208 "will return the -`skip`th log entry. Equivalent to get_next(-skip).");
209 static PyObject *
210 Journal_get_previous(Journal *self, PyObject *args)
211 {
212     int64_t skip=1LL;
213     if (! PyArg_ParseTuple(args, "|L", &skip))
214         return NULL;
215
216     return PyObject_CallMethod((PyObject *)self, "get_next", "L", -skip);
217 }
218
219 PyDoc_STRVAR(Journal_add_match__doc__,
220 "add_match(match, ..., field=value, ...) -> None\n\n"
221 "Add a match to filter journal log entries. All matches of different\n"
222 "field are combined in logical AND, and matches of the same field\n"
223 "are automatically combined in logical OR.\n"
224 "Matches can be passed as strings \"field=value\", or keyword\n"
225 "arguments field=\"value\".");
226 static PyObject *
227 Journal_add_match(Journal *self, PyObject *args, PyObject *keywds)
228 {
229     Py_ssize_t arg_match_len;
230     char *arg_match;
231     int i, r;
232     for (i = 0; i < PySequence_Size(args); i++) {
233 #if PY_MAJOR_VERSION >=3
234         PyObject *arg;
235         arg = PySequence_Fast_GET_ITEM(args, i);
236         if (PyUnicode_Check(arg)) {
237 #if PY_MINOR_VERSION >=3
238             arg_match = PyUnicode_AsUTF8AndSize(arg, &arg_match_len);
239 #else
240             PyObject *temp;
241             temp = PyUnicode_AsUTF8String(arg);
242             PyBytes_AsStringAndSize(temp, &arg_match, &arg_match_len);
243             Py_DECREF(temp);
244 #endif
245         }else if (PyBytes_Check(arg)) {
246             PyBytes_AsStringAndSize(arg, &arg_match, &arg_match_len);
247         }else{
248             PyErr_SetString(PyExc_TypeError, "expected bytes or string");
249         }
250 #else
251         PyString_AsStringAndSize(PySequence_Fast_GET_ITEM(args, i), &arg_match, &arg_match_len);
252 #endif
253         if (PyErr_Occurred())
254             return NULL;
255         r = sd_journal_add_match(self->j, arg_match, arg_match_len);
256         if (r < 0) {
257             errno = -r;
258             PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
259                                 r == -ENOMEM ? PyExc_MemoryError :
260                                 PyExc_OSError;
261             PyErr_SetFromErrno(errtype);
262             return NULL;
263         }
264     }
265
266     if (! keywds)
267         Py_RETURN_NONE;
268
269     PyObject *key, *value;
270     Py_ssize_t pos=0, match_key_len, match_value_len;
271     int match_len;
272     char *match_key, *match_value;
273     void *match;
274     while (PyDict_Next(keywds, &pos, &key, &value)) {
275 #if PY_MAJOR_VERSION >=3
276         if (PyUnicode_Check(key)) {
277 #if PY_MINOR_VERSION >=3
278             match_key = PyUnicode_AsUTF8AndSize(key, &match_key_len);
279 #else
280             PyObject *temp2;
281             temp2 = PyUnicode_AsUTF8String(key);
282             PyBytes_AsStringAndSize(temp2, &match_key, &match_key_len);
283             Py_DECREF(temp2);
284 #endif
285         }else if (PyBytes_Check(key)) {
286             PyBytes_AsStringAndSize(key, &match_key, &match_key_len);
287         }else{
288             PyErr_SetString(PyExc_TypeError, "expected bytes or string");
289         }
290         if (PyUnicode_Check(value)) {
291 #if PY_MINOR_VERSION >=3
292             match_value = PyUnicode_AsUTF8AndSize(value, &match_value_len);
293 #else
294             PyObject *temp3;
295             temp3 = PyUnicode_AsUTF8String(value);
296             PyBytes_AsStringAndSize(temp3, &match_value, &match_value_len);
297             Py_DECREF(temp3);
298 #endif
299         }else if (PyBytes_Check(value)) {
300             PyBytes_AsStringAndSize(value, &match_value, &match_value_len);
301         }else{
302             PyErr_SetString(PyExc_TypeError, "expected bytes or string");
303         }
304 #else
305         PyString_AsStringAndSize(key, &match_key, &match_key_len);
306         PyString_AsStringAndSize(value, &match_value, &match_value_len);
307 #endif
308         if (PyErr_Occurred())
309             return NULL;
310
311         match_len = match_key_len + 1 + match_value_len;
312         match = malloc(match_len);
313         memcpy(match, match_key, match_key_len);
314         memcpy(match + match_key_len, "=", 1);
315         memcpy(match + match_key_len + 1, match_value, match_value_len);
316
317         r = sd_journal_add_match(self->j, match, match_len);
318         free(match);
319         if (r == -EINVAL) {
320             PyErr_SetString(PyExc_ValueError, "Invalid match");
321             return NULL;
322         }else if (r == -ENOMEM) {
323             PyErr_SetString(PyExc_MemoryError, "Not enough memory");
324             return NULL;
325         }else if (r < 0) {
326             PyErr_SetString(PyExc_RuntimeError, "Error adding match");
327             return NULL;
328         }
329     }
330
331     Py_RETURN_NONE;
332 }
333
334 PyDoc_STRVAR(Journal_add_disjunction__doc__,
335 "add_disjunction() -> None\n\n"
336 "Once called, all matches before and after are combined in logical\n"
337 "OR.");
338 static PyObject *
339 Journal_add_disjunction(Journal *self, PyObject *args)
340 {
341     int r;
342     r = sd_journal_add_disjunction(self->j);
343     if (r < 0) {
344         errno = -r;
345         PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError :
346                             PyExc_OSError;
347         PyErr_SetFromErrno(errtype);
348         return NULL;
349     }
350     Py_RETURN_NONE;
351 }
352
353 PyDoc_STRVAR(Journal_flush_matches__doc__,
354 "flush_matches() -> None\n\n"
355 "Clears all current match filters.");
356 static PyObject *
357 Journal_flush_matches(Journal *self, PyObject *args)
358 {
359     sd_journal_flush_matches(self->j);
360     Py_RETURN_NONE;
361 }
362
363 PyDoc_STRVAR(Journal_seek__doc__,
364 "seek(offset[, whence]) -> None\n\n"
365 "Seek through journal by `offset` number of entries. Argument\n"
366 "`whence` defines what the offset is relative to:\n"
367 "os.SEEK_SET (default) from first match in journal;\n"
368 "os.SEEK_CUR from current position in journal;\n"
369 "and os.SEEK_END is from last match in journal.");
370 static PyObject *
371 Journal_seek(Journal *self, PyObject *args, PyObject *keywds)
372 {
373     int64_t offset;
374     int whence=SEEK_SET;
375     static char *kwlist[] = {"offset", "whence", NULL};
376
377     if (! PyArg_ParseTupleAndKeywords(args, keywds, "L|i", kwlist,
378                                       &offset, &whence))
379         return NULL;
380
381     PyObject *result=NULL;
382     if (whence == SEEK_SET){
383         int r;
384         Py_BEGIN_ALLOW_THREADS
385         r = sd_journal_seek_head(self->j);
386         Py_END_ALLOW_THREADS
387         if (r < 0) {
388             errno = -r;
389             PyErr_SetFromErrno(PyExc_OSError);
390             return NULL;
391         }
392         if (offset > 0LL) {
393             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
394         }
395     }else if (whence == SEEK_CUR){
396         result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
397     }else if (whence == SEEK_END){
398         int r;
399         Py_BEGIN_ALLOW_THREADS
400         r = sd_journal_seek_tail(self->j);
401         Py_END_ALLOW_THREADS
402         if (r < 0) {
403             errno = -r;
404             PyErr_SetFromErrno(PyExc_OSError);
405             return NULL;
406         }
407         if (offset < 0LL) {
408             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", offset);
409         }else{
410             result = PyObject_CallMethod((PyObject *)self, "get_next", "L", -1LL);
411         }
412     }else{
413         PyErr_SetString(PyExc_ValueError, "Invalid value for whence");
414     }
415
416     if (result)
417         Py_DECREF(result);
418     if (PyErr_Occurred())
419         return NULL;
420     Py_RETURN_NONE;
421 }
422
423 PyDoc_STRVAR(Journal_seek_realtime__doc__,
424 "seek_realtime(realtime) -> None\n\n"
425 "Seek to nearest matching journal entry to `realtime`. Argument\n"
426 "`realtime` can be an integer unix timestamp in usecs or a "
427 "datetime instance.");
428 static PyObject *
429 Journal_seek_realtime(Journal *self, PyObject *args)
430 {
431     uint64_t timestamp;
432     if (! PyArg_ParseTuple(args, "K", &timestamp))
433         return NULL;
434
435     if ((int64_t) timestamp < 0LL) {
436         PyErr_SetString(PyExc_ValueError, "Time must be positive integer");
437         return NULL;
438     }
439
440     int r;
441     Py_BEGIN_ALLOW_THREADS
442     r = sd_journal_seek_realtime_usec(self->j, timestamp);
443     Py_END_ALLOW_THREADS
444     if (r < 0) {
445         errno = -r;
446         PyErr_SetFromErrno(PyExc_OSError);
447         return NULL;
448     }
449     Py_RETURN_NONE;
450 }
451
452 PyDoc_STRVAR(Journal_seek_monotonic__doc__,
453 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
454 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
455 "`monotonic` is an timestamp from boot in secs, or a\n"
456 "timedelta instance.\n"
457 "Argument `bootid` is a string representing which boot the\n"
458 "monotonic time is reference to. Defaults to current bootid.");
459 static PyObject *
460 Journal_seek_monotonic(Journal *self, PyObject *args)
461 {
462     double timedouble;
463     char *bootid=NULL;
464     if (! PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
465         return NULL;
466
467     uint64_t timestamp;
468     timestamp = (uint64_t) (timedouble * 1.0E6);
469
470     if ((int64_t) timestamp < 0LL) {
471         PyErr_SetString(PyExc_ValueError, "Time must be positive number");
472         return NULL;
473     }
474
475     sd_id128_t sd_id;
476     int r;
477     if (bootid) {
478         r = sd_id128_from_string(bootid, &sd_id);
479         if (r == -EINVAL) {
480             PyErr_SetString(PyExc_ValueError, "Invalid bootid");
481             return NULL;
482         }else if (r < 0) {
483             errno = -r;
484             PyErr_SetFromErrno(PyExc_OSError);
485             return NULL;
486         }
487     }else{
488         r = sd_id128_get_boot(&sd_id);
489         if (r == -EIO) {
490             PyErr_SetString(PyExc_IOError, "Error getting current boot ID");
491             return NULL;
492         }else if (r < 0) {
493             errno = -r;
494             PyErr_SetFromErrno(PyExc_OSError);
495             return NULL;
496         }
497     }
498
499     Py_BEGIN_ALLOW_THREADS
500     r = sd_journal_seek_monotonic_usec(self->j, sd_id, timestamp);
501     Py_END_ALLOW_THREADS
502     if (r < 0) {
503         errno = -r;
504         PyErr_SetFromErrno(PyExc_OSError);
505         return NULL;
506     }
507     Py_RETURN_NONE;
508 }
509  
510 PyDoc_STRVAR(Journal_wait__doc__,
511 "wait([timeout]) -> Change state (integer)\n\n"
512 "Waits until there is a change in the journal. Argument `timeout`\n"
513 "is the maximum number of seconds to wait before returning\n"
514 "regardless if journal has changed. If `timeout` is not given or is\n"
515 "0, then it will block forever.\n"
516 "Will return constants: NOP if no change; APPEND if new\n"
517 "entries have been added to the end of the journal; and\n"
518 "INVALIDATE if journal files have been added or removed.");
519 static PyObject *
520 Journal_wait(Journal *self, PyObject *args, PyObject *keywds)
521 {
522     int64_t timeout=0LL;
523     if (! PyArg_ParseTuple(args, "|L", &timeout))
524         return NULL;
525
526     int r;
527     Py_BEGIN_ALLOW_THREADS
528     if ( timeout == 0LL) {
529         r = sd_journal_wait(self->j, (uint64_t) -1);
530     }else{
531         r = sd_journal_wait(self->j, timeout * 1E6);
532     }
533     Py_END_ALLOW_THREADS
534     if (r < 0) {
535         errno = -r;
536         PyObject *errtype = r == -ENOMEM ? PyExc_MemoryError :
537                             PyExc_OSError;
538         PyErr_SetFromErrno(errtype);
539         return NULL;
540     }
541 #if PY_MAJOR_VERSION >=3
542     return PyLong_FromLong(r);
543 #else
544     return PyInt_FromLong(r);
545 #endif
546 }
547
548 PyDoc_STRVAR(Journal_seek_cursor__doc__,
549 "seek_cursor(cursor) -> None\n\n"
550 "Seeks to journal entry by given unique reference `cursor`.");
551 static PyObject *
552 Journal_seek_cursor(Journal *self, PyObject *args)
553 {
554     const char *cursor;
555     if (! PyArg_ParseTuple(args, "s", &cursor))
556         return NULL;
557
558     int r;
559     Py_BEGIN_ALLOW_THREADS
560     r = sd_journal_seek_cursor(self->j, cursor);
561     Py_END_ALLOW_THREADS
562     if (r < 0) {
563         errno = -r;
564         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
565                             r == -ENOMEM ? PyExc_MemoryError :
566                             PyExc_OSError;
567         PyErr_SetFromErrno(errtype);
568         return NULL;
569     }
570     Py_RETURN_NONE;
571 }
572
573 static PyObject *
574 Journal_iter(PyObject *self)
575 {
576     Py_INCREF(self);
577     return self;
578 }
579
580 static PyObject *
581 Journal_iternext(PyObject *self)
582 {
583     PyObject *dict;
584     Py_ssize_t dict_size;
585
586     dict = PyObject_CallMethod(self, "get_next", "");
587     dict_size = PyDict_Size(dict);
588     if ((int64_t) dict_size > 0LL) {
589         return dict;
590     }else{
591         Py_DECREF(dict);
592         PyErr_SetNone(PyExc_StopIteration);
593         return NULL;
594     }
595 }
596
597 #ifdef SD_JOURNAL_FOREACH_UNIQUE
598 PyDoc_STRVAR(Journal_query_unique__doc__,
599 "query_unique(field) -> a set of values\n\n"
600 "Returns a set of unique values in journal for given `field`.\n"
601 "Note this does not respect any journal matches.");
602 static PyObject *
603 Journal_query_unique(Journal *self, PyObject *args)
604 {
605     char *query;
606     if (! PyArg_ParseTuple(args, "s", &query))
607         return NULL;
608
609     int r;
610     Py_BEGIN_ALLOW_THREADS
611     r = sd_journal_query_unique(self->j, query);
612     Py_END_ALLOW_THREADS
613     if (r < 0) {
614         errno = -r;
615         PyObject *errtype = r == -EINVAL ? PyExc_ValueError :
616                             r == -ENOMEM ? PyExc_MemoryError :
617                             PyExc_OSError;
618         PyErr_SetFromErrno(errtype);
619         return NULL;
620     }
621
622     const void *uniq;
623     size_t uniq_len;
624     const char *delim_ptr;
625     PyObject *value_set, *key, *value;
626     value_set = PySet_New(0);
627
628 #if PY_MAJOR_VERSION >=3
629     key = PyUnicode_FromString(query);
630 #else
631     key = PyString_FromString(query);
632 #endif
633
634     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
635         delim_ptr = memchr(uniq, '=', uniq_len);
636         value = PyBytes_FromStringAndSize(delim_ptr + 1, (const char*) uniq + uniq_len - (delim_ptr + 1));
637         PySet_Add(value_set, value);
638         Py_DECREF(value);
639     }
640     Py_DECREF(key);
641     return value_set;
642 }
643 #endif //def SD_JOURNAL_FOREACH_UNIQUE
644
645 static PyObject *
646 Journal_get_data_threshold(Journal *self, void *closure)
647 {
648     size_t cvalue;
649     PyObject *value;
650     int r;
651
652     r = sd_journal_get_data_threshold(self->j, &cvalue);
653     if (r < 0) {
654         errno = -r;
655         PyErr_SetFromErrno(PyExc_OSError);
656         return NULL;
657     }
658
659 #if PY_MAJOR_VERSION >=3
660     value = PyLong_FromSize_t(cvalue);
661 #else
662     value = PyInt_FromSize_t(cvalue);
663 #endif
664     return value;
665 }
666
667 static int
668 Journal_set_data_threshold(Journal *self, PyObject *value, void *closure)
669 {
670     if (value == NULL) {
671         PyErr_SetString(PyExc_TypeError, "Cannot delete data threshold");
672         return -1;
673     }
674 #if PY_MAJOR_VERSION >=3
675     if (! PyLong_Check(value)){
676 #else
677     if (! PyInt_Check(value)){
678 #endif
679         PyErr_SetString(PyExc_TypeError, "Data threshold must be int");
680         return -1;
681     }
682     int r;
683 #if PY_MAJOR_VERSION >=3
684     r = sd_journal_set_data_threshold(self->j, (size_t) PyLong_AsLong(value));
685 #else
686     r = sd_journal_set_data_threshold(self->j, (size_t) PyInt_AsLong(value));
687 #endif
688     if (r < 0) {
689         errno = -r;
690         PyErr_SetFromErrno(PyExc_OSError);
691         return NULL;
692     }
693     return 0;
694 }
695
696 static PyGetSetDef Journal_getseters[] = {
697     {"data_threshold",
698     (getter)Journal_get_data_threshold,
699     (setter)Journal_set_data_threshold,
700     "data threshold",
701     NULL},
702     {NULL}
703 };
704
705 static PyMethodDef Journal_methods[] = {
706     {"get_next", (PyCFunction)Journal_get_next, METH_VARARGS,
707     Journal_get_next__doc__},
708     {"get_previous", (PyCFunction)Journal_get_previous, METH_VARARGS,
709     Journal_get_previous__doc__},
710     {"add_match", (PyCFunction)Journal_add_match, METH_VARARGS|METH_KEYWORDS,
711     Journal_add_match__doc__},
712     {"add_disjunction", (PyCFunction)Journal_add_disjunction, METH_NOARGS,
713     Journal_add_disjunction__doc__},
714     {"flush_matches", (PyCFunction)Journal_flush_matches, METH_NOARGS,
715     Journal_flush_matches__doc__},
716     {"seek", (PyCFunction)Journal_seek, METH_VARARGS | METH_KEYWORDS,
717     Journal_seek__doc__},
718     {"seek_realtime", (PyCFunction)Journal_seek_realtime, METH_VARARGS,
719     Journal_seek_realtime__doc__},
720     {"seek_monotonic", (PyCFunction)Journal_seek_monotonic, METH_VARARGS,
721     Journal_seek_monotonic__doc__},
722     {"wait", (PyCFunction)Journal_wait, METH_VARARGS,
723     Journal_wait__doc__},
724     {"seek_cursor", (PyCFunction)Journal_seek_cursor, METH_VARARGS,
725     Journal_seek_cursor__doc__},
726 #ifdef SD_JOURNAL_FOREACH_UNIQUE
727     {"query_unique", (PyCFunction)Journal_query_unique, METH_VARARGS,
728     Journal_query_unique__doc__},
729 #endif
730     {NULL}  /* Sentinel */
731 };
732
733 static PyTypeObject JournalType = {
734     PyVarObject_HEAD_INIT(NULL, 0)
735     "_reader.Journal",           /*tp_name*/
736     sizeof(Journal),                  /*tp_basicsize*/
737     0,                                /*tp_itemsize*/
738     (destructor)Journal_dealloc,      /*tp_dealloc*/
739     0,                                /*tp_print*/
740     0,                                /*tp_getattr*/
741     0,                                /*tp_setattr*/
742     0,                                /*tp_compare*/
743     0,                                /*tp_repr*/
744     0,                                /*tp_as_number*/
745     0,                                /*tp_as_sequence*/
746     0,                                /*tp_as_mapping*/
747     0,                                /*tp_hash */
748     0,                                /*tp_call*/
749     0,                                /*tp_str*/
750     0,                                /*tp_getattro*/
751     0,                                /*tp_setattro*/
752     0,                                /*tp_as_buffer*/
753     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/
754     Journal__doc__,                   /* tp_doc */
755     0,                                /* tp_traverse */
756     0,                                /* tp_clear */
757     0,                                /* tp_richcompare */
758     0,                                /* tp_weaklistoffset */
759     Journal_iter,                     /* tp_iter */
760     Journal_iternext,                 /* tp_iternext */
761     Journal_methods,                  /* tp_methods */
762     0,                                /* tp_members */
763     Journal_getseters,                /* tp_getset */
764     0,                                /* tp_base */
765     0,                                /* tp_dict */
766     0,                                /* tp_descr_get */
767     0,                                /* tp_descr_set */
768     0,                                /* tp_dictoffset */
769     (initproc)Journal_init,           /* tp_init */
770     0,                                /* tp_alloc */
771     PyType_GenericNew,                /* tp_new */
772 };
773
774 #if PY_MAJOR_VERSION >= 3
775 static PyModuleDef _reader_module = {
776     PyModuleDef_HEAD_INIT,
777     "_reader",
778     "Module that reads systemd journal similar to journalctl.",
779     -1,
780     NULL, NULL, NULL, NULL, NULL
781 };
782 #endif
783
784 PyMODINIT_FUNC
785 #if PY_MAJOR_VERSION >= 3
786 PyInit__reader(void)
787 #else
788 init_reader(void) 
789 #endif
790 {
791     PyObject* m;
792
793     PyDateTime_IMPORT;
794
795     if (PyType_Ready(&JournalType) < 0)
796 #if PY_MAJOR_VERSION >= 3
797         return NULL;
798 #else
799         return;
800 #endif
801
802 #if PY_MAJOR_VERSION >= 3
803     m = PyModule_Create(&_reader_module);
804     if (m == NULL)
805         return NULL;
806 #else
807     m = Py_InitModule3("_reader", NULL,
808                    "Module that reads systemd journal similar to journalctl.");
809     if (m == NULL)
810         return;
811 #endif
812
813     Py_INCREF(&JournalType);
814     PyModule_AddObject(m, "_Journal", (PyObject *)&JournalType);
815     PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP);
816     PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND);
817     PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE);
818     PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY);
819     PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY);
820     PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY);
821
822 #if PY_MAJOR_VERSION >= 3
823     return m;
824 #endif
825 }