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