chiark / gitweb /
systemd-python: export new sd_journal_{process,get_events,get_timeout}
[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
34 typedef struct {
35     PyObject_HEAD
36     sd_journal *j;
37 } Reader;
38 static PyTypeObject ReaderType;
39
40 static int set_error(int r, const char* path, const char* invalid_message) {
41     if (r >= 0)
42         return r;
43     if (r == -EINVAL && invalid_message)
44         PyErr_SetString(PyExc_ValueError, invalid_message);
45     else if (r == -ENOMEM)
46         PyErr_SetString(PyExc_MemoryError, "Not enough memory");
47     else {
48         errno = -r;
49         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
50     }
51     return -1;
52 }
53
54
55 PyDoc_STRVAR(module__doc__,
56              "Class to reads the systemd journal similar to journalctl.");
57
58
59 #if PY_MAJOR_VERSION >= 3
60 static PyTypeObject MonotonicType;
61
62 PyDoc_STRVAR(MonotonicType__doc__,
63              "A tuple of (timestamp, bootid) for holding monotonic timestamps");
64
65 static PyStructSequence_Field MonotonicType_fields[] = {
66     {(char*) "timestamp", (char*) "Time"},
67     {(char*) "bootid", (char*) "Unique identifier of the boot"},
68     {} /* Sentinel */
69 };
70
71 static PyStructSequence_Desc Monotonic_desc = {
72     (char*) "journal.Monotonic",
73     MonotonicType__doc__,
74     MonotonicType_fields,
75     2,
76 };
77 #endif
78
79
80 static void Reader_dealloc(Reader* self)
81 {
82     sd_journal_close(self->j);
83     Py_TYPE(self)->tp_free((PyObject*)self);
84 }
85
86 PyDoc_STRVAR(Reader__doc__,
87              "_Reader([flags | path]) -> ...\n\n"
88              "_Reader allows filtering and retrieval of Journal entries.\n"
89              "Note: this is a low-level interface, and probably not what you\n"
90              "want, use systemd.journal.Reader instead.\n\n"
91              "Argument `flags` sets open flags of the journal, which can be one\n"
92              "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
93              "journal on local machine only; RUNTIME_ONLY opens only\n"
94              "volatile journal files; and SYSTEM_ONLY opens only\n"
95              "journal files of system services and the kernel.\n\n"
96              "Argument `path` is the directory of journal files. Note that\n"
97              "`flags` and `path` are exclusive.\n\n"
98              "_Reader implements the context manager protocol: the journal\n"
99              "will be closed when exiting the block.");
100 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
101 {
102     int flags = 0, r;
103     char *path = NULL;
104
105     static const char* const kwlist[] = {"flags", "path", NULL};
106     if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
107                                      &flags, &path))
108         return -1;
109
110     if (!flags)
111         flags = SD_JOURNAL_LOCAL_ONLY;
112     else
113         if (path) {
114             PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
115             return -1;
116         }
117
118     Py_BEGIN_ALLOW_THREADS
119     if (path)
120         r = sd_journal_open_directory(&self->j, path, 0);
121     else
122         r = sd_journal_open(&self->j, flags);
123     Py_END_ALLOW_THREADS
124
125     return set_error(r, path, "Invalid flags or path");
126 }
127
128
129 PyDoc_STRVAR(Reader_fileno__doc__,
130              "fileno() -> int\n\n"
131              "Get a file descriptor to poll for changes in the journal.\n"
132              "This method invokes sd_journal_get_fd().\n"
133              "See man:sd_journal_get_fd(3).");
134 static PyObject* Reader_fileno(Reader *self, PyObject *args)
135 {
136     int 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 = sd_journal_reliable_fd(self->j);
152     set_error(r, NULL, NULL);
153     if (r < 0)
154         return NULL;
155     return PyBool_FromLong(r);
156 }
157
158
159 PyDoc_STRVAR(Reader_get_events__doc__,
160              "get_events() -> int\n\n"
161              "Returns a mask of poll() events to wait for on the file\n"
162              "descriptor returned by .fileno().\n\n"
163              "See man:sd_journal_get_events(3) for further discussion.");
164 static PyObject* Reader_get_events(Reader *self, PyObject *args)
165 {
166     int r = sd_journal_get_events(self->j);
167     set_error(r, NULL, NULL);
168     if (r < 0)
169         return NULL;
170     return long_FromLong(r);
171 }
172
173
174 PyDoc_STRVAR(Reader_get_timeout__doc__,
175              "get_timeout() -> int or None\n\n"
176              "Returns a timeout value for usage in poll(), the time since the\n"
177              "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
178              "is necessary.\n\n"
179              "The return value must be converted to a relative timeout in \n"
180              "milliseconds if it is to be used as an argument for poll().\n"
181              "See man:sd_journal_get_timeout(3) for further discussion.");
182 static PyObject* Reader_get_timeout(Reader *self, PyObject *args)
183 {
184     int r;
185     uint64_t t;
186
187     r = sd_journal_get_timeout(self->j, &t);
188     set_error(r, NULL, NULL);
189     if (r < 0)
190         return NULL;
191
192     if (t == (uint64_t) -1)
193         Py_RETURN_NONE;
194
195     assert_cc(sizeof(unsigned long long) == sizeof(t));
196     return PyLong_FromUnsignedLongLong(t);
197 }
198
199
200 PyDoc_STRVAR(Reader_get_timeout_ms__doc__,
201              "get_timeout_ms() -> int\n\n"
202              "Returns a timeout value suitable for usage in poll(), the value\n"
203              "returned by .get_timeout() converted to relative ms, or -1 if\n"
204              "no timeout is necessary.");
205 static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args)
206 {
207     int r;
208     uint64_t t;
209
210     r = sd_journal_get_timeout(self->j, &t);
211     set_error(r, NULL, NULL);
212     if (r < 0)
213         return NULL;
214
215     if (t == (uint64_t) -1)
216         return PyLong_FromLong(-1);
217     else {
218         struct timespec ts;
219         uint64_t n;
220         int msec;
221
222         clock_gettime(CLOCK_MONOTONIC, &ts);
223         n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
224         msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
225
226         return PyLong_FromLong(msec);
227     }
228 }
229
230
231 PyDoc_STRVAR(Reader_close__doc__,
232              "close() -> None\n\n"
233              "Free resources allocated by this Reader object.\n"
234              "This method invokes sd_journal_close().\n"
235              "See man:sd_journal_close(3).");
236 static PyObject* Reader_close(Reader *self, PyObject *args)
237 {
238     assert(self);
239     assert(!args);
240
241     sd_journal_close(self->j);
242     self->j = NULL;
243     Py_RETURN_NONE;
244 }
245
246
247 PyDoc_STRVAR(Reader_get_usage__doc__,
248              "get_usage() -> int\n\n"
249              "Returns the total disk space currently used by journal\n"
250              "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
251              "passed when opening the journal this value will only reflect\n"
252              "the size of journal files of the local host, otherwise\n"
253              "of all hosts.\n\n"
254              "This method invokes sd_journal_get_usage().\n"
255              "See man:sd_journal_get_usage(3).");
256 static PyObject* Reader_get_usage(Reader *self, PyObject *args)
257 {
258     int r;
259     uint64_t bytes;
260
261     r = sd_journal_get_usage(self->j, &bytes);
262     if (set_error(r, NULL, NULL))
263         return NULL;
264
265     assert_cc(sizeof(unsigned long long) == sizeof(bytes));
266     return PyLong_FromUnsignedLongLong(bytes);
267 }
268
269
270 PyDoc_STRVAR(Reader___enter____doc__,
271              "__enter__() -> self\n\n"
272              "Part of the context manager protocol.\n"
273              "Returns self.\n");
274 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
275 {
276     assert(self);
277     assert(!args);
278
279     Py_INCREF(self);
280     return self;
281 }
282
283 PyDoc_STRVAR(Reader___exit____doc__,
284              "__exit__(type, value, traceback) -> None\n\n"
285              "Part of the context manager protocol.\n"
286              "Closes the journal.\n");
287 static PyObject* Reader___exit__(Reader *self, PyObject *args)
288 {
289     assert(self);
290
291     sd_journal_close(self->j);
292     self->j = NULL;
293     Py_RETURN_NONE;
294 }
295
296
297 PyDoc_STRVAR(Reader_next__doc__,
298              "next([skip]) -> bool\n\n"
299              "Go to the next log entry. Optional skip value means to go to\n"
300              "the `skip`\\-th log entry.\n"
301              "Returns False if at end of file, True otherwise.");
302 static PyObject* Reader_next(Reader *self, PyObject *args)
303 {
304     int64_t skip = 1LL;
305     int r;
306
307     if (!PyArg_ParseTuple(args, "|L:next", &skip))
308         return NULL;
309
310     if (skip == 0LL) {
311         PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
312         return NULL;
313     }
314
315     Py_BEGIN_ALLOW_THREADS
316     if (skip == 1LL)
317         r = sd_journal_next(self->j);
318     else if (skip == -1LL)
319         r = sd_journal_previous(self->j);
320     else if (skip > 1LL)
321         r = sd_journal_next_skip(self->j, skip);
322     else if (skip < -1LL)
323         r = sd_journal_previous_skip(self->j, -skip);
324     else
325         assert_not_reached("should not be here");
326     Py_END_ALLOW_THREADS
327
328     set_error(r, NULL, NULL);
329     if (r < 0)
330         return NULL;
331     return PyBool_FromLong(r);
332 }
333
334
335 static int extract(const char* msg, size_t msg_len,
336                    PyObject **key, PyObject **value) {
337     PyObject *k = NULL, *v;
338     const char *delim_ptr;
339
340     delim_ptr = memchr(msg, '=', msg_len);
341     if (!delim_ptr) {
342         PyErr_SetString(PyExc_OSError,
343                         "journal gave us a field without '='");
344         return -1;
345     }
346
347     if (key) {
348         k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
349         if (!k)
350             return -1;
351     }
352
353     if (value) {
354         v = PyBytes_FromStringAndSize(delim_ptr + 1,
355                              (const char*) msg + msg_len - (delim_ptr + 1));
356         if (!v) {
357             Py_XDECREF(k);
358             return -1;
359         }
360
361         *value = v;
362     }
363
364     if (key)
365         *key = k;
366
367     return 0;
368 }
369
370 PyDoc_STRVAR(Reader_get__doc__,
371              "get(str) -> str\n\n"
372              "Return data associated with this key in current log entry.\n"
373              "Throws KeyError is the data is not available.");
374 static PyObject* Reader_get(Reader *self, PyObject *args)
375 {
376     const char* field;
377     const void* msg;
378     size_t msg_len;
379     PyObject *value;
380     int r;
381
382     assert(self);
383     assert(args);
384
385     if (!PyArg_ParseTuple(args, "s:get", &field))
386         return NULL;
387
388     r = sd_journal_get_data(self->j, field, &msg, &msg_len);
389     if (r == -ENOENT) {
390         PyErr_SetString(PyExc_KeyError, field);
391         return NULL;
392     } else if (set_error(r, NULL, "field name is not valid"))
393         return NULL;
394
395     r = extract(msg, msg_len, NULL, &value);
396     if (r < 0)
397         return NULL;
398     return value;
399 }
400
401
402 PyDoc_STRVAR(Reader_get_next__doc__,
403              "get_next([skip]) -> dict\n\n"
404              "Return dictionary of the next log entry. Optional skip value will\n"
405              "return the `skip`\\-th log entry. Returns an empty dict on EOF.");
406 static PyObject* Reader_get_next(Reader *self, PyObject *args)
407 {
408     PyObject _cleanup_Py_DECREF_ *tmp = NULL;
409     PyObject *dict;
410     const void *msg;
411     size_t msg_len;
412     int r;
413
414     tmp = Reader_next(self, args);
415     if (!tmp)
416         return NULL;
417     if (tmp == Py_False) /* EOF */
418         return PyDict_New();
419
420     dict = PyDict_New();
421     if (!dict)
422             return NULL;
423
424     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
425         PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
426
427         r = extract(msg, msg_len, &key, &value);
428         if (r < 0)
429             goto error;
430
431         if (PyDict_Contains(dict, key)) {
432             PyObject *cur_value = PyDict_GetItem(dict, key);
433
434             if (PyList_CheckExact(cur_value)) {
435                 r = PyList_Append(cur_value, value);
436                 if (r < 0)
437                     goto error;
438             } else {
439                 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
440                 if (!tmp_list)
441                     goto error;
442
443                 r = PyList_Append(tmp_list, cur_value);
444                 if (r < 0)
445                     goto error;
446
447                 r = PyList_Append(tmp_list, value);
448                 if (r < 0)
449                     goto error;
450
451                 r = PyDict_SetItem(dict, key, tmp_list);
452                 if (r < 0)
453                     goto error;
454             }
455         } else {
456             r = PyDict_SetItem(dict, key, value);
457             if (r < 0)
458                 goto error;
459         }
460     }
461
462     return dict;
463
464 error:
465     Py_DECREF(dict);
466     return NULL;
467 }
468
469
470 PyDoc_STRVAR(Reader_get_realtime__doc__,
471              "get_realtime() -> int\n\n"
472              "Return the realtime timestamp for the current journal entry\n"
473              "in microseconds.\n\n"
474              "Wraps sd_journal_get_realtime_usec().\n"
475              "See man:sd_journal_get_realtime_usec(3).");
476 static PyObject* Reader_get_realtime(Reader *self, PyObject *args)
477 {
478     uint64_t timestamp;
479     int r;
480
481     assert(self);
482     assert(!args);
483
484     r = sd_journal_get_realtime_usec(self->j, &timestamp);
485     if (set_error(r, NULL, NULL))
486         return NULL;
487
488     assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
489     return PyLong_FromUnsignedLongLong(timestamp);
490 }
491
492
493 PyDoc_STRVAR(Reader_get_monotonic__doc__,
494              "get_monotonic() -> (timestamp, bootid)\n\n"
495              "Return the monotonic timestamp for the current journal entry\n"
496              "as a tuple of time in microseconds and bootid.\n\n"
497              "Wraps sd_journal_get_monotonic_usec().\n"
498              "See man:sd_journal_get_monotonic_usec(3).");
499 static PyObject* Reader_get_monotonic(Reader *self, PyObject *args)
500 {
501     uint64_t timestamp;
502     sd_id128_t id;
503     PyObject *monotonic, *bootid, *tuple;
504     int r;
505
506     assert(self);
507     assert(!args);
508
509     r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
510     if (set_error(r, NULL, NULL))
511         return NULL;
512
513     assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
514     monotonic = PyLong_FromUnsignedLongLong(timestamp);
515     bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
516 #if PY_MAJOR_VERSION >= 3
517     tuple = PyStructSequence_New(&MonotonicType);
518 #else
519     tuple = PyTuple_New(2);
520 #endif
521     if (!monotonic || !bootid || !tuple) {
522         Py_XDECREF(monotonic);
523         Py_XDECREF(bootid);
524         Py_XDECREF(tuple);
525         return NULL;
526     }
527
528 #if PY_MAJOR_VERSION >= 3
529     PyStructSequence_SET_ITEM(tuple, 0, monotonic);
530     PyStructSequence_SET_ITEM(tuple, 1, bootid);
531 #else
532     PyTuple_SET_ITEM(tuple, 0, monotonic);
533     PyTuple_SET_ITEM(tuple, 1, bootid);
534 #endif
535
536     return tuple;
537 }
538
539
540 PyDoc_STRVAR(Reader_get_previous__doc__,
541              "get_previous([skip]) -> dict\n\n"
542              "Return dictionary of the previous log entry. Optional skip value\n"
543              "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
544 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
545 {
546     int64_t skip = 1LL;
547     if (!PyArg_ParseTuple(args, "|L:get_previous", &skip))
548         return NULL;
549
550     return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
551                                (char*) "L", -skip);
552 }
553
554
555 PyDoc_STRVAR(Reader_add_match__doc__,
556              "add_match(match) -> None\n\n"
557              "Add a match to filter journal log entries. All matches of different\n"
558              "fields are combined with logical AND, and matches of the same field\n"
559              "are automatically combined with logical OR.\n"
560              "Match is a string of the form \"FIELD=value\".");
561 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
562 {
563     char *match;
564     int match_len, r;
565     if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
566         return NULL;
567
568     r = sd_journal_add_match(self->j, match, match_len);
569     set_error(r, NULL, "Invalid match");
570     if (r < 0)
571             return NULL;
572
573     Py_RETURN_NONE;
574 }
575
576
577 PyDoc_STRVAR(Reader_add_disjunction__doc__,
578              "add_disjunction() -> None\n\n"
579              "Inserts a logical OR between matches added before and afterwards.");
580 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
581 {
582     int r;
583     r = sd_journal_add_disjunction(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     char _cleanup_free_ *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
810 static PyObject* Reader_iter(PyObject *self)
811 {
812     Py_INCREF(self);
813     return self;
814 }
815
816 static PyObject* Reader_iternext(PyObject *self)
817 {
818     PyObject *dict;
819     Py_ssize_t dict_size;
820
821     dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
822     if (PyErr_Occurred())
823         return NULL;
824     dict_size = PyDict_Size(dict);
825     if ((int64_t) dict_size > 0LL) {
826         return dict;
827     } else {
828         Py_DECREF(dict);
829         PyErr_SetNone(PyExc_StopIteration);
830         return NULL;
831     }
832 }
833
834
835 PyDoc_STRVAR(Reader_query_unique__doc__,
836              "query_unique(field) -> a set of values\n\n"
837              "Return a set of unique values appearing in journal for the\n"
838              "given `field`. Note this does not respect any journal matches.");
839 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
840 {
841     char *query;
842     int r;
843     const void *uniq;
844     size_t uniq_len;
845     PyObject *value_set, *key, *value;
846
847     if (!PyArg_ParseTuple(args, "s:query_unique", &query))
848         return NULL;
849
850     Py_BEGIN_ALLOW_THREADS
851     r = sd_journal_query_unique(self->j, query);
852     Py_END_ALLOW_THREADS
853     if (set_error(r, NULL, "Invalid field name"))
854         return NULL;
855
856     value_set = PySet_New(0);
857     key = unicode_FromString(query);
858
859     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
860         const char *delim_ptr;
861
862         delim_ptr = memchr(uniq, '=', uniq_len);
863         value = PyBytes_FromStringAndSize(
864             delim_ptr + 1,
865             (const char*) uniq + uniq_len - (delim_ptr + 1));
866         PySet_Add(value_set, value);
867         Py_DECREF(value);
868     }
869     Py_DECREF(key);
870     return value_set;
871 }
872
873
874 PyDoc_STRVAR(Reader_get_catalog__doc__,
875              "get_catalog() -> str\n\n"
876              "Retrieve a message catalog entry for the current journal entry.\n"
877              "Will throw IndexError if the entry has no MESSAGE_ID\n"
878              "and KeyError is the id is specified, but hasn't been found\n"
879              "in the catalog.\n\n"
880              "Wraps man:sd_journal_get_catalog(3).");
881 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
882 {
883     int r;
884     char _cleanup_free_ *msg = NULL;
885
886     assert(self);
887     assert(!args);
888
889     Py_BEGIN_ALLOW_THREADS
890     r = sd_journal_get_catalog(self->j, &msg);
891     Py_END_ALLOW_THREADS
892     if (r == -ENOENT) {
893         const void* mid;
894         size_t mid_len;
895
896         r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
897         if (r == 0) {
898             const int l = sizeof("MESSAGE_ID");
899             assert(mid_len > l);
900             PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l,
901                          (const char*) mid + l);
902         } else if (r == -ENOENT)
903             PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
904         else
905             set_error(r, NULL, NULL);
906         return NULL;
907     } else if (set_error(r, NULL, NULL))
908         return NULL;
909
910     return unicode_FromString(msg);
911 }
912
913
914 PyDoc_STRVAR(get_catalog__doc__,
915              "get_catalog(id128) -> str\n\n"
916              "Retrieve a message catalog entry for the given id.\n"
917              "Wraps man:sd_journal_get_catalog_for_message_id(3).");
918 static PyObject* get_catalog(PyObject *self, PyObject *args)
919 {
920     int r;
921     char *id_ = NULL;
922     sd_id128_t id;
923     char _cleanup_free_ *msg = NULL;
924
925     assert(!self);
926     assert(args);
927
928     if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
929         return NULL;
930
931     r = sd_id128_from_string(id_, &id);
932     if (set_error(r, NULL, "Invalid id128"))
933         return NULL;
934
935     Py_BEGIN_ALLOW_THREADS
936     r = sd_journal_get_catalog_for_message_id(id, &msg);
937     Py_END_ALLOW_THREADS
938     if (set_error(r, NULL, NULL))
939         return NULL;
940
941     return unicode_FromString(msg);
942 }
943
944
945 PyDoc_STRVAR(data_threshold__doc__,
946              "Threshold for field size truncation in bytes.\n\n"
947              "Fields longer than this will be truncated to the threshold size.\n"
948              "Defaults to 64Kb.");
949
950 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
951 {
952     size_t cvalue;
953     int r;
954
955     r = sd_journal_get_data_threshold(self->j, &cvalue);
956     if (set_error(r, NULL, NULL))
957         return NULL;
958
959     return long_FromSize_t(cvalue);
960 }
961
962 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
963 {
964     int r;
965     if (value == NULL) {
966         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
967         return -1;
968     }
969     if (!long_Check(value)){
970         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
971         return -1;
972     }
973     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
974     return set_error(r, NULL, NULL);
975 }
976
977
978 PyDoc_STRVAR(closed__doc__,
979              "True iff journal is closed");
980 static PyObject* Reader_get_closed(Reader *self, void *closure)
981 {
982     return PyBool_FromLong(self->j == NULL);
983 }
984
985
986 static PyGetSetDef Reader_getsetters[] = {
987     {(char*) "data_threshold",
988      (getter) Reader_get_data_threshold,
989      (setter) Reader_set_data_threshold,
990      (char*) data_threshold__doc__,
991      NULL},
992     {(char*) "closed",
993      (getter) Reader_get_closed,
994      NULL,
995      (char*) closed__doc__,
996      NULL},
997     {} /* Sentinel */
998 };
999
1000 static PyMethodDef Reader_methods[] = {
1001     {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
1002     {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
1003     {"get_events",      (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
1004     {"get_timeout",     (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
1005     {"get_timeout_ms",  (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
1006     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
1007     {"get_usage",       (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
1008     {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
1009     {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
1010     {"next",            (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
1011     {"get",             (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
1012     {"get_next",        (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
1013     {"get_previous",    (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
1014     {"get_realtime",    (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
1015     {"get_monotonic",   (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
1016     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
1017     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
1018     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
1019     {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
1020     {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
1021     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
1022     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
1023     {"process",         (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
1024     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
1025     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
1026     {"get_cursor",      (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
1027     {"test_cursor",     (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
1028     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
1029     {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
1030     {}  /* Sentinel */
1031 };
1032
1033 static PyTypeObject ReaderType = {
1034     PyVarObject_HEAD_INIT(NULL, 0)
1035     "_reader._Reader",                        /*tp_name*/
1036     sizeof(Reader),                           /*tp_basicsize*/
1037     0,                                        /*tp_itemsize*/
1038     (destructor)Reader_dealloc,               /*tp_dealloc*/
1039     0,                                        /*tp_print*/
1040     0,                                        /*tp_getattr*/
1041     0,                                        /*tp_setattr*/
1042     0,                                        /*tp_compare*/
1043     0,                                        /*tp_repr*/
1044     0,                                        /*tp_as_number*/
1045     0,                                        /*tp_as_sequence*/
1046     0,                                        /*tp_as_mapping*/
1047     0,                                        /*tp_hash */
1048     0,                                        /*tp_call*/
1049     0,                                        /*tp_str*/
1050     0,                                        /*tp_getattro*/
1051     0,                                        /*tp_setattro*/
1052     0,                                        /*tp_as_buffer*/
1053     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1054     Reader__doc__,                            /* tp_doc */
1055     0,                                        /* tp_traverse */
1056     0,                                        /* tp_clear */
1057     0,                                        /* tp_richcompare */
1058     0,                                        /* tp_weaklistoffset */
1059     Reader_iter,                              /* tp_iter */
1060     Reader_iternext,                          /* tp_iternext */
1061     Reader_methods,                           /* tp_methods */
1062     0,                                        /* tp_members */
1063     Reader_getsetters,                        /* tp_getset */
1064     0,                                        /* tp_base */
1065     0,                                        /* tp_dict */
1066     0,                                        /* tp_descr_get */
1067     0,                                        /* tp_descr_set */
1068     0,                                        /* tp_dictoffset */
1069     (initproc) Reader_init,                   /* tp_init */
1070     0,                                        /* tp_alloc */
1071     PyType_GenericNew,                        /* tp_new */
1072 };
1073
1074 static PyMethodDef methods[] = {
1075         { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
1076         { NULL, NULL, 0, NULL }        /* Sentinel */
1077 };
1078
1079 #if PY_MAJOR_VERSION >= 3
1080 static PyModuleDef module = {
1081     PyModuleDef_HEAD_INIT,
1082     "_reader",
1083     module__doc__,
1084     -1,
1085     methods,
1086     NULL, NULL, NULL, NULL
1087 };
1088 #endif
1089
1090 #if PY_MAJOR_VERSION >= 3
1091 static bool initialized = false;
1092 #endif
1093
1094 #pragma GCC diagnostic push
1095 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
1096
1097 PyMODINIT_FUNC
1098 #if PY_MAJOR_VERSION >= 3
1099 PyInit__reader(void)
1100 #else
1101 init_reader(void)
1102 #endif
1103 {
1104     PyObject* m;
1105
1106     PyDateTime_IMPORT;
1107
1108     if (PyType_Ready(&ReaderType) < 0)
1109 #if PY_MAJOR_VERSION >= 3
1110         return NULL;
1111 #else
1112         return;
1113 #endif
1114
1115 #if PY_MAJOR_VERSION >= 3
1116     m = PyModule_Create(&module);
1117     if (m == NULL)
1118         return NULL;
1119
1120     if (!initialized) {
1121         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
1122         initialized = true;
1123     }
1124 #else
1125     m = Py_InitModule3("_reader", methods, module__doc__);
1126     if (m == NULL)
1127         return;
1128 #endif
1129
1130     Py_INCREF(&ReaderType);
1131 #if PY_MAJOR_VERSION >= 3
1132     Py_INCREF(&MonotonicType);
1133 #endif
1134     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
1135 #if PY_MAJOR_VERSION >= 3
1136         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
1137 #endif
1138         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
1139         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
1140         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
1141         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
1142         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
1143         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
1144 #if PY_MAJOR_VERSION >= 3
1145         Py_DECREF(m);
1146         return NULL;
1147 #endif
1148     }
1149
1150 #if PY_MAJOR_VERSION >= 3
1151     return m;
1152 #endif
1153 }
1154
1155 #pragma GCC diagnostic pop