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