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