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