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