chiark / gitweb /
systemd-python: fix formatting in docstring
[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     {} /* Sentinel */
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\n"
97              "_Reader implements the context manager protocol: the journal\n"
98              "will be closed when exiting the block.");
99 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
100 {
101     int flags = 0, r;
102     char *path = NULL;
103
104     static const char* const kwlist[] = {"flags", "path", NULL};
105     if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
106                                      &flags, &path))
107         return -1;
108
109     if (!flags)
110         flags = SD_JOURNAL_LOCAL_ONLY;
111     else
112         if (path) {
113             PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
114             return -1;
115         }
116
117     Py_BEGIN_ALLOW_THREADS
118     if (path)
119         r = sd_journal_open_directory(&self->j, path, 0);
120     else
121         r = sd_journal_open(&self->j, flags);
122     Py_END_ALLOW_THREADS
123
124     return set_error(r, path, "Invalid flags or path");
125 }
126
127
128 PyDoc_STRVAR(Reader_fileno__doc__,
129              "fileno() -> int\n\n"
130              "Get a file descriptor to poll for changes in the journal.\n"
131              "This method invokes sd_journal_get_fd().\n"
132              "See man:sd_journal_get_fd(3).");
133 static PyObject* Reader_fileno(Reader *self, PyObject *args)
134 {
135     int fd;
136     fd = sd_journal_get_fd(self->j);
137     set_error(fd, NULL, NULL);
138     if (fd < 0)
139         return NULL;
140     return long_FromLong(fd);
141 }
142
143
144 PyDoc_STRVAR(Reader_reliable_fd__doc__,
145              "reliable_fd() -> bool\n\n"
146              "Returns True iff the journal can be polled reliably.\n"
147              "This method invokes sd_journal_reliable_fd().\n"
148              "See man:sd_journal_reliable_fd(3).");
149 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
150 {
151     int r;
152     r = sd_journal_reliable_fd(self->j);
153     set_error(r, NULL, NULL);
154     if (r < 0)
155         return NULL;
156     return PyBool_FromLong(r);
157 }
158
159
160 PyDoc_STRVAR(Reader_close__doc__,
161              "close() -> None\n\n"
162              "Free resources allocated by this Reader object.\n"
163              "This method invokes sd_journal_close().\n"
164              "See man:sd_journal_close(3).");
165 static PyObject* Reader_close(Reader *self, PyObject *args)
166 {
167     assert(self);
168     assert(!args);
169
170     sd_journal_close(self->j);
171     self->j = NULL;
172     Py_RETURN_NONE;
173 }
174
175
176 PyDoc_STRVAR(Reader_get_usage__doc__,
177              "get_usage() -> int\n\n"
178              "Returns the total disk space currently used by journal\n"
179              "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
180              "passed when opening the journal this value will only reflect\n"
181              "the size of journal files of the local host, otherwise\n"
182              "of all hosts.\n\n"
183              "This method invokes sd_journal_get_usage().\n"
184              "See man:sd_journal_get_usage(3).");
185 static PyObject* Reader_get_usage(Reader *self, PyObject *args)
186 {
187     int r;
188     uint64_t bytes;
189
190     r = sd_journal_get_usage(self->j, &bytes);
191     if (set_error(r, NULL, NULL))
192         return NULL;
193
194     assert_cc(sizeof(unsigned long long) == sizeof(bytes));
195     return PyLong_FromUnsignedLongLong(bytes);
196 }
197
198
199 PyDoc_STRVAR(Reader___enter____doc__,
200              "__enter__() -> self\n\n"
201              "Part of the context manager protocol.\n"
202              "Returns self.\n");
203 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
204 {
205     assert(self);
206     assert(!args);
207
208     Py_INCREF(self);
209     return self;
210 }
211
212 PyDoc_STRVAR(Reader___exit____doc__,
213              "__exit__(type, value, traceback) -> None\n\n"
214              "Part of the context manager protocol.\n"
215              "Closes the journal.\n");
216 static PyObject* Reader___exit__(Reader *self, PyObject *args)
217 {
218     assert(self);
219
220     sd_journal_close(self->j);
221     self->j = NULL;
222     Py_RETURN_NONE;
223 }
224
225
226 PyDoc_STRVAR(Reader_next__doc__,
227              "next([skip]) -> bool\n\n"
228              "Go to the next log entry. Optional skip value means to go to\n"
229              "the `skip`\\-th log entry.\n"
230              "Returns False if at end of file, True otherwise.");
231 static PyObject* Reader_next(Reader *self, PyObject *args)
232 {
233     int64_t skip = 1LL;
234     int r;
235
236     if (!PyArg_ParseTuple(args, "|L:next", &skip))
237         return NULL;
238
239     if (skip == 0LL) {
240         PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
241         return NULL;
242     }
243
244     Py_BEGIN_ALLOW_THREADS
245     if (skip == 1LL)
246         r = sd_journal_next(self->j);
247     else if (skip == -1LL)
248         r = sd_journal_previous(self->j);
249     else if (skip > 1LL)
250         r = sd_journal_next_skip(self->j, skip);
251     else if (skip < -1LL)
252         r = sd_journal_previous_skip(self->j, -skip);
253     else
254         assert_not_reached("should not be here");
255     Py_END_ALLOW_THREADS
256
257     set_error(r, NULL, NULL);
258     if (r < 0)
259         return NULL;
260     return PyBool_FromLong(r);
261 }
262
263
264 static int extract(const char* msg, size_t msg_len,
265                    PyObject **key, PyObject **value) {
266     PyObject *k = NULL, *v;
267     const char *delim_ptr;
268
269     delim_ptr = memchr(msg, '=', msg_len);
270     if (!delim_ptr) {
271         PyErr_SetString(PyExc_OSError,
272                         "journal gave us a field without '='");
273         return -1;
274     }
275
276     if (key) {
277         k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
278         if (!k)
279             return -1;
280     }
281
282     if (value) {
283         v = PyBytes_FromStringAndSize(delim_ptr + 1,
284                              (const char*) msg + msg_len - (delim_ptr + 1));
285         if (!v) {
286             Py_XDECREF(k);
287             return -1;
288         }
289
290         *value = v;
291     }
292
293     if (key)
294         *key = k;
295
296     return 0;
297 }
298
299 PyDoc_STRVAR(Reader_get__doc__,
300              "get(str) -> str\n\n"
301              "Return data associated with this key in current log entry.\n"
302              "Throws KeyError is the data is not available.");
303 static PyObject* Reader_get(Reader *self, PyObject *args)
304 {
305     const char* field;
306     const void* msg;
307     size_t msg_len;
308     PyObject *value;
309     int r;
310
311     assert(self);
312     assert(args);
313
314     if (!PyArg_ParseTuple(args, "s:get", &field))
315         return NULL;
316
317     r = sd_journal_get_data(self->j, field, &msg, &msg_len);
318     if (r == -ENOENT) {
319         PyErr_SetString(PyExc_KeyError, field);
320         return NULL;
321     } else if (set_error(r, NULL, "field name is not valid"))
322         return NULL;
323
324     r = extract(msg, msg_len, NULL, &value);
325     if (r < 0)
326         return NULL;
327     return value;
328 }
329
330
331 PyDoc_STRVAR(Reader_get_next__doc__,
332              "get_next([skip]) -> dict\n\n"
333              "Return dictionary of the next log entry. Optional skip value will\n"
334              "return the `skip`\\-th log entry. Returns an empty dict on EOF.");
335 static PyObject* Reader_get_next(Reader *self, PyObject *args)
336 {
337     PyObject _cleanup_Py_DECREF_ *tmp = NULL;
338     PyObject *dict;
339     const void *msg;
340     size_t msg_len;
341     int r;
342
343     tmp = Reader_next(self, args);
344     if (!tmp)
345         return NULL;
346     if (tmp == Py_False) /* EOF */
347         return PyDict_New();
348
349     dict = PyDict_New();
350     if (!dict)
351             return NULL;
352
353     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
354         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
355
356         r = extract(msg, msg_len, &key, &value);
357         if (r < 0)
358             goto error;
359
360         if (PyDict_Contains(dict, key)) {
361             PyObject *cur_value = PyDict_GetItem(dict, key);
362
363             if (PyList_CheckExact(cur_value)) {
364                 r = PyList_Append(cur_value, value);
365                 if (r < 0)
366                     goto error;
367             } else {
368                 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
369                 if (!tmp_list)
370                     goto error;
371
372                 r = PyList_Append(tmp_list, cur_value);
373                 if (r < 0)
374                     goto error;
375
376                 r = PyList_Append(tmp_list, value);
377                 if (r < 0)
378                     goto error;
379
380                 r = PyDict_SetItem(dict, key, tmp_list);
381                 if (r < 0)
382                     goto error;
383             }
384         } else {
385             r = PyDict_SetItem(dict, key, value);
386             if (r < 0)
387                 goto error;
388         }
389     }
390
391     return dict;
392
393 error:
394     Py_DECREF(dict);
395     return NULL;
396 }
397
398
399 PyDoc_STRVAR(Reader_get_realtime__doc__,
400              "get_realtime() -> int\n\n"
401              "Return the realtime timestamp for the current journal entry\n"
402              "in microseconds.\n\n"
403              "Wraps sd_journal_get_realtime_usec().\n"
404              "See man:sd_journal_get_realtime_usec(3).");
405 static PyObject* Reader_get_realtime(Reader *self, PyObject *args)
406 {
407     uint64_t timestamp;
408     int r;
409
410     assert(self);
411     assert(!args);
412
413     r = sd_journal_get_realtime_usec(self->j, &timestamp);
414     if (set_error(r, NULL, NULL))
415         return NULL;
416
417     assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
418     return PyLong_FromUnsignedLongLong(timestamp);
419 }
420
421
422 PyDoc_STRVAR(Reader_get_monotonic__doc__,
423              "get_monotonic() -> (timestamp, bootid)\n\n"
424              "Return the monotonic timestamp for the current journal entry\n"
425              "as a tuple of time in microseconds and bootid.\n\n"
426              "Wraps sd_journal_get_monotonic_usec().\n"
427              "See man:sd_journal_get_monotonic_usec(3).");
428 static PyObject* Reader_get_monotonic(Reader *self, PyObject *args)
429 {
430     uint64_t timestamp;
431     sd_id128_t id;
432     PyObject *monotonic, *bootid, *tuple;
433     int r;
434
435     assert(self);
436     assert(!args);
437
438     r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
439     if (set_error(r, NULL, NULL))
440         return NULL;
441
442     assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
443     monotonic = PyLong_FromUnsignedLongLong(timestamp);
444     bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
445 #if PY_MAJOR_VERSION >= 3
446     tuple = PyStructSequence_New(&MonotonicType);
447 #else
448     tuple = PyTuple_New(2);
449 #endif
450     if (!monotonic || !bootid || !tuple) {
451         Py_XDECREF(monotonic);
452         Py_XDECREF(bootid);
453         Py_XDECREF(tuple);
454         return NULL;
455     }
456
457 #if PY_MAJOR_VERSION >= 3
458     PyStructSequence_SET_ITEM(tuple, 0, monotonic);
459     PyStructSequence_SET_ITEM(tuple, 1, bootid);
460 #else
461     PyTuple_SET_ITEM(tuple, 0, monotonic);
462     PyTuple_SET_ITEM(tuple, 1, bootid);
463 #endif
464
465     return tuple;
466 }
467
468
469 PyDoc_STRVAR(Reader_get_previous__doc__,
470              "get_previous([skip]) -> dict\n\n"
471              "Return dictionary of the previous log entry. Optional skip value\n"
472              "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
473 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
474 {
475     int64_t skip = 1LL;
476     if (!PyArg_ParseTuple(args, "|L:get_previous", &skip))
477         return NULL;
478
479     return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
480                                (char*) "L", -skip);
481 }
482
483
484 PyDoc_STRVAR(Reader_add_match__doc__,
485              "add_match(match) -> None\n\n"
486              "Add a match to filter journal log entries. All matches of different\n"
487              "fields are combined with logical AND, and matches of the same field\n"
488              "are automatically combined with logical OR.\n"
489              "Match is a string of the form \"FIELD=value\".");
490 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
491 {
492     char *match;
493     int match_len, r;
494     if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
495         return NULL;
496
497     r = sd_journal_add_match(self->j, match, match_len);
498     set_error(r, NULL, "Invalid match");
499     if (r < 0)
500             return NULL;
501
502     Py_RETURN_NONE;
503 }
504
505
506 PyDoc_STRVAR(Reader_add_disjunction__doc__,
507              "add_disjunction() -> None\n\n"
508              "Inserts a logical OR between matches added before and afterwards.");
509 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
510 {
511     int r;
512     r = sd_journal_add_disjunction(self->j);
513     set_error(r, NULL, NULL);
514     if (r < 0)
515         return NULL;
516     Py_RETURN_NONE;
517 }
518
519
520 PyDoc_STRVAR(Reader_flush_matches__doc__,
521              "flush_matches() -> None\n\n"
522              "Clear all current match filters.");
523 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
524 {
525     sd_journal_flush_matches(self->j);
526     Py_RETURN_NONE;
527 }
528
529
530 PyDoc_STRVAR(Reader_seek_head__doc__,
531              "seek_head() -> None\n\n"
532              "Jump to the beginning of the journal.\n"
533              "This method invokes sd_journal_seek_head().\n"
534              "See man:sd_journal_seek_head(3).");
535 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
536 {
537     int r;
538     Py_BEGIN_ALLOW_THREADS
539     r = sd_journal_seek_head(self->j);
540     Py_END_ALLOW_THREADS
541     if (set_error(r, NULL, NULL))
542         return NULL;
543     Py_RETURN_NONE;
544 }
545
546
547 PyDoc_STRVAR(Reader_seek_tail__doc__,
548              "seek_tail() -> None\n\n"
549              "Jump to the end of the journal.\n"
550              "This method invokes sd_journal_seek_tail().\n"
551              "See man:sd_journal_seek_tail(3).");
552 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
553 {
554     int r;
555     Py_BEGIN_ALLOW_THREADS
556     r = sd_journal_seek_tail(self->j);
557     Py_END_ALLOW_THREADS
558     if (set_error(r, NULL, NULL))
559         return NULL;
560     Py_RETURN_NONE;
561 }
562
563
564 PyDoc_STRVAR(Reader_seek_realtime__doc__,
565              "seek_realtime(realtime) -> None\n\n"
566              "Seek to nearest matching journal entry to `realtime`. Argument\n"
567              "`realtime` in specified in seconds.");
568 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
569 {
570     uint64_t timestamp;
571     int r;
572
573     if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
574         return NULL;
575
576     Py_BEGIN_ALLOW_THREADS
577     r = sd_journal_seek_realtime_usec(self->j, timestamp);
578     Py_END_ALLOW_THREADS
579     if (set_error(r, NULL, NULL))
580         return NULL;
581     Py_RETURN_NONE;
582 }
583
584
585 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
586              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
587              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
588              "`monotonic` is an timestamp from boot in microseconds.\n"
589              "Argument `bootid` is a string representing which boot the\n"
590              "monotonic time is reference to. Defaults to current bootid.");
591 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
592 {
593     char *bootid = NULL;
594     uint64_t timestamp;
595     sd_id128_t id;
596     int r;
597
598     if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
599         return NULL;
600
601     if (bootid) {
602         r = sd_id128_from_string(bootid, &id);
603         if (set_error(r, NULL, "Invalid bootid"))
604             return NULL;
605     } else {
606         Py_BEGIN_ALLOW_THREADS
607         r = sd_id128_get_boot(&id);
608         Py_END_ALLOW_THREADS
609         if (set_error(r, NULL, NULL))
610             return NULL;
611     }
612
613     Py_BEGIN_ALLOW_THREADS
614     r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
615     Py_END_ALLOW_THREADS
616     if (set_error(r, NULL, NULL))
617         return NULL;
618
619     Py_RETURN_NONE;
620 }
621
622
623 PyDoc_STRVAR(Reader_wait__doc__,
624              "wait([timeout]) -> state change (integer)\n\n"
625              "Wait for a change in the journal. Argument `timeout` specifies\n"
626              "the maximum number of microseconds to wait before returning\n"
627              "regardless of wheter the journal has changed. If `timeout` is -1,\n"
628              "then block forever.\n\n"
629              "Will return constants: NOP if no change; APPEND if new\n"
630              "entries have been added to the end of the journal; and\n"
631              "INVALIDATE if journal files have been added or removed.\n\n"
632              "See man:sd_journal_wait(3) for further discussion.");
633 static PyObject* Reader_wait(Reader *self, PyObject *args)
634 {
635     int r;
636     int64_t timeout;
637
638     if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
639         return NULL;
640
641     Py_BEGIN_ALLOW_THREADS
642     r = sd_journal_wait(self->j, timeout);
643     Py_END_ALLOW_THREADS
644     if (set_error(r, NULL, NULL) < 0)
645         return NULL;
646
647     return long_FromLong(r);
648 }
649
650
651 PyDoc_STRVAR(Reader_seek_cursor__doc__,
652              "seek_cursor(cursor) -> None\n\n"
653              "Seek to journal entry by given unique reference `cursor`.");
654 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
655 {
656     const char *cursor;
657     int r;
658
659     if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
660         return NULL;
661
662     Py_BEGIN_ALLOW_THREADS
663     r = sd_journal_seek_cursor(self->j, cursor);
664     Py_END_ALLOW_THREADS
665     if (set_error(r, NULL, "Invalid cursor"))
666         return NULL;
667     Py_RETURN_NONE;
668 }
669
670
671 PyDoc_STRVAR(Reader_get_cursor__doc__,
672              "get_cursor() -> str\n\n"
673              "Return a cursor string for the current journal entry.\n\n"
674              "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
675 static PyObject* Reader_get_cursor(Reader *self, PyObject *args)
676 {
677     char _cleanup_free_ *cursor = NULL;
678     int r;
679
680     assert(self);
681     assert(!args);
682
683     r = sd_journal_get_cursor(self->j, &cursor);
684     if (set_error(r, NULL, NULL))
685         return NULL;
686
687     return unicode_FromString(cursor);
688 }
689
690
691 PyDoc_STRVAR(Reader_test_cursor__doc__,
692              "test_cursor(str) -> bool\n\n"
693              "Test whether the cursor string matches current journal entry.\n\n"
694              "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
695 static PyObject* Reader_test_cursor(Reader *self, PyObject *args)
696 {
697     const char *cursor;
698     int r;
699
700     assert(self);
701     assert(args);
702
703     if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
704         return NULL;
705
706     r = sd_journal_test_cursor(self->j, cursor);
707     set_error(r, NULL, NULL);
708     if (r < 0)
709         return NULL;
710
711     return PyBool_FromLong(r);
712 }
713
714
715 static PyObject* Reader_iter(PyObject *self)
716 {
717     Py_INCREF(self);
718     return self;
719 }
720
721 static PyObject* Reader_iternext(PyObject *self)
722 {
723     PyObject *dict;
724     Py_ssize_t dict_size;
725
726     dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
727     if (PyErr_Occurred())
728         return NULL;
729     dict_size = PyDict_Size(dict);
730     if ((int64_t) dict_size > 0LL) {
731         return dict;
732     } else {
733         Py_DECREF(dict);
734         PyErr_SetNone(PyExc_StopIteration);
735         return NULL;
736     }
737 }
738
739
740 PyDoc_STRVAR(Reader_query_unique__doc__,
741              "query_unique(field) -> a set of values\n\n"
742              "Return a set of unique values appearing in journal for the\n"
743              "given `field`. Note this does not respect any journal matches.");
744 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
745 {
746     char *query;
747     int r;
748     const void *uniq;
749     size_t uniq_len;
750     PyObject *value_set, *key, *value;
751
752     if (!PyArg_ParseTuple(args, "s:query_unique", &query))
753         return NULL;
754
755     Py_BEGIN_ALLOW_THREADS
756     r = sd_journal_query_unique(self->j, query);
757     Py_END_ALLOW_THREADS
758     if (set_error(r, NULL, "Invalid field name"))
759         return NULL;
760
761     value_set = PySet_New(0);
762     key = unicode_FromString(query);
763
764     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
765         const char *delim_ptr;
766
767         delim_ptr = memchr(uniq, '=', uniq_len);
768         value = PyBytes_FromStringAndSize(
769             delim_ptr + 1,
770             (const char*) uniq + uniq_len - (delim_ptr + 1));
771         PySet_Add(value_set, value);
772         Py_DECREF(value);
773     }
774     Py_DECREF(key);
775     return value_set;
776 }
777
778
779 PyDoc_STRVAR(Reader_get_catalog__doc__,
780              "get_catalog() -> str\n\n"
781              "Retrieve a message catalog entry for the current journal entry.\n"
782              "Will throw IndexError if the entry has no MESSAGE_ID\n"
783              "and KeyError is the id is specified, but hasn't been found\n"
784              "in the catalog.\n\n"
785              "Wraps man:sd_journal_get_catalog(3).");
786 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
787 {
788     int r;
789     char _cleanup_free_ *msg = NULL;
790
791     assert(self);
792     assert(!args);
793
794     Py_BEGIN_ALLOW_THREADS
795     r = sd_journal_get_catalog(self->j, &msg);
796     Py_END_ALLOW_THREADS
797     if (r == -ENOENT) {
798         const void* mid;
799         size_t mid_len;
800
801         r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
802         if (r == 0) {
803             const int l = sizeof("MESSAGE_ID");
804             assert(mid_len > l);
805             PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l,
806                          (const char*) mid + l);
807         } else if (r == -ENOENT)
808             PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
809         else
810             set_error(r, NULL, NULL);
811         return NULL;
812     } else if (set_error(r, NULL, NULL))
813         return NULL;
814
815     return unicode_FromString(msg);
816 }
817
818
819 PyDoc_STRVAR(get_catalog__doc__,
820              "get_catalog(id128) -> str\n\n"
821              "Retrieve a message catalog entry for the given id.\n"
822              "Wraps man:sd_journal_get_catalog_for_message_id(3).");
823 static PyObject* get_catalog(PyObject *self, PyObject *args)
824 {
825     int r;
826     char *id_ = NULL;
827     sd_id128_t id;
828     char _cleanup_free_ *msg = NULL;
829
830     assert(!self);
831     assert(args);
832
833     if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
834         return NULL;
835
836     r = sd_id128_from_string(id_, &id);
837     if (set_error(r, NULL, "Invalid id128"))
838         return NULL;
839
840     Py_BEGIN_ALLOW_THREADS
841     r = sd_journal_get_catalog_for_message_id(id, &msg);
842     Py_END_ALLOW_THREADS
843     if (set_error(r, NULL, NULL))
844         return NULL;
845
846     return unicode_FromString(msg);
847 }
848
849
850 PyDoc_STRVAR(data_threshold__doc__,
851              "Threshold for field size truncation in bytes.\n\n"
852              "Fields longer than this will be truncated to the threshold size.\n"
853              "Defaults to 64Kb.");
854
855 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
856 {
857     size_t cvalue;
858     int r;
859
860     r = sd_journal_get_data_threshold(self->j, &cvalue);
861     if (set_error(r, NULL, NULL))
862         return NULL;
863
864     return long_FromSize_t(cvalue);
865 }
866
867 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
868 {
869     int r;
870     if (value == NULL) {
871         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
872         return -1;
873     }
874     if (!long_Check(value)){
875         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
876         return -1;
877     }
878     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
879     return set_error(r, NULL, NULL);
880 }
881
882
883 PyDoc_STRVAR(closed__doc__,
884              "True iff journal is closed");
885 static PyObject* Reader_get_closed(Reader *self, void *closure)
886 {
887     return PyBool_FromLong(self->j == NULL);
888 }
889
890
891 static PyGetSetDef Reader_getsetters[] = {
892     {(char*) "data_threshold",
893      (getter) Reader_get_data_threshold,
894      (setter) Reader_set_data_threshold,
895      (char*) data_threshold__doc__,
896      NULL},
897     {(char*) "closed",
898      (getter) Reader_get_closed,
899      NULL,
900      (char*) closed__doc__,
901      NULL},
902     {} /* Sentinel */
903 };
904
905 static PyMethodDef Reader_methods[] = {
906     {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
907     {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
908     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
909     {"get_usage",       (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
910     {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
911     {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
912     {"next",            (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
913     {"get",             (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
914     {"get_next",        (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
915     {"get_previous",    (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
916     {"get_realtime",    (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
917     {"get_monotonic",   (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
918     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
919     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
920     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
921     {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
922     {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
923     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
924     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
925     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
926     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
927     {"get_cursor",      (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
928     {"test_cursor",     (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
929     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
930     {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
931     {}  /* Sentinel */
932 };
933
934 static PyTypeObject ReaderType = {
935     PyVarObject_HEAD_INIT(NULL, 0)
936     "_reader._Reader",                        /*tp_name*/
937     sizeof(Reader),                           /*tp_basicsize*/
938     0,                                        /*tp_itemsize*/
939     (destructor)Reader_dealloc,               /*tp_dealloc*/
940     0,                                        /*tp_print*/
941     0,                                        /*tp_getattr*/
942     0,                                        /*tp_setattr*/
943     0,                                        /*tp_compare*/
944     0,                                        /*tp_repr*/
945     0,                                        /*tp_as_number*/
946     0,                                        /*tp_as_sequence*/
947     0,                                        /*tp_as_mapping*/
948     0,                                        /*tp_hash */
949     0,                                        /*tp_call*/
950     0,                                        /*tp_str*/
951     0,                                        /*tp_getattro*/
952     0,                                        /*tp_setattro*/
953     0,                                        /*tp_as_buffer*/
954     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
955     Reader__doc__,                            /* tp_doc */
956     0,                                        /* tp_traverse */
957     0,                                        /* tp_clear */
958     0,                                        /* tp_richcompare */
959     0,                                        /* tp_weaklistoffset */
960     Reader_iter,                              /* tp_iter */
961     Reader_iternext,                          /* tp_iternext */
962     Reader_methods,                           /* tp_methods */
963     0,                                        /* tp_members */
964     Reader_getsetters,                        /* tp_getset */
965     0,                                        /* tp_base */
966     0,                                        /* tp_dict */
967     0,                                        /* tp_descr_get */
968     0,                                        /* tp_descr_set */
969     0,                                        /* tp_dictoffset */
970     (initproc) Reader_init,                   /* tp_init */
971     0,                                        /* tp_alloc */
972     PyType_GenericNew,                        /* tp_new */
973 };
974
975 static PyMethodDef methods[] = {
976         { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
977         { NULL, NULL, 0, NULL }        /* Sentinel */
978 };
979
980 #if PY_MAJOR_VERSION >= 3
981 static PyModuleDef module = {
982     PyModuleDef_HEAD_INIT,
983     "_reader",
984     module__doc__,
985     -1,
986     methods,
987     NULL, NULL, NULL, NULL
988 };
989 #endif
990
991 #if PY_MAJOR_VERSION >= 3
992 static bool initialized = false;
993 #endif
994
995 #pragma GCC diagnostic push
996 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
997
998 PyMODINIT_FUNC
999 #if PY_MAJOR_VERSION >= 3
1000 PyInit__reader(void)
1001 #else
1002 init_reader(void)
1003 #endif
1004 {
1005     PyObject* m;
1006
1007     PyDateTime_IMPORT;
1008
1009     if (PyType_Ready(&ReaderType) < 0)
1010 #if PY_MAJOR_VERSION >= 3
1011         return NULL;
1012 #else
1013         return;
1014 #endif
1015
1016 #if PY_MAJOR_VERSION >= 3
1017     m = PyModule_Create(&module);
1018     if (m == NULL)
1019         return NULL;
1020
1021     if (!initialized) {
1022         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
1023         initialized = true;
1024     }
1025 #else
1026     m = Py_InitModule3("_reader", methods, module__doc__);
1027     if (m == NULL)
1028         return;
1029 #endif
1030
1031     Py_INCREF(&ReaderType);
1032 #if PY_MAJOR_VERSION >= 3
1033     Py_INCREF(&MonotonicType);
1034 #endif
1035     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
1036 #if PY_MAJOR_VERSION >= 3
1037         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
1038 #endif
1039         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
1040         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
1041         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
1042         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
1043         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
1044         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
1045 #if PY_MAJOR_VERSION >= 3
1046         Py_DECREF(m);
1047         return NULL;
1048 #endif
1049     }
1050
1051 #if PY_MAJOR_VERSION >= 3
1052     return m;
1053 #endif
1054 }
1055
1056 #pragma GCC diagnostic pop