4444# define HAVE_HAS_PERSISTENT_FILES
4545#endif
4646
47+ #if LIBSYSTEMD_VERSION >= 230
48+ # define HAVE_JOURNAL_OPEN_DIRECTORY_FD
49+ #endif
50+
4751typedef struct {
4852 PyObject_HEAD
4953 sd_journal * j ;
@@ -75,22 +79,49 @@ static PyStructSequence_Desc Monotonic_desc = {
7579#endif
7680
7781/**
78- * Convert a Python sequence object into a strv (char**), and
79- * None into a NULL pointer .
82+ * Convert a str or bytes object into a C-string path.
83+ * Returns NULL on error .
8084 */
81- static int strv_converter (PyObject * obj , void * _result ) {
82- char * * * result = _result ;
83- Py_ssize_t i , len ;
85+ static char * convert_path (PyObject * path , PyObject * * bytes ) {
86+ #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
87+ int r ;
88+
89+ r = PyUnicode_FSConverter (path , bytes );
90+ if (r == 0 )
91+ return NULL ;
92+
93+ return PyBytes_AsString (* bytes );
94+ #else
95+ return PyString_AsString (path );
96+ #endif
97+ }
98+
99+ /**
100+ * Return NULL is obj is None, and the object otherwise.
101+ */
102+ static int null_converter (PyObject * obj , void * _result ) {
103+ PyObject * * result = _result ;
84104
85105 assert (result );
86106
87107 if (!obj )
88108 return 0 ;
89109
90- if (obj == Py_None ) {
110+ if (obj == Py_None )
91111 * result = NULL ;
92- return 1 ;
93- }
112+ else
113+ * result = obj ;
114+ return 1 ;
115+ }
116+
117+ /**
118+ * Convert a Python sequence object into a strv (char**).
119+ */
120+ static int strv_converter (PyObject * obj , void * _result ) {
121+ char * * * result = _result ;
122+ Py_ssize_t i , len ;
123+
124+ assert (result );
94125
95126 if (!PySequence_Check (obj ))
96127 return 0 ;
@@ -104,32 +135,21 @@ static int strv_converter(PyObject* obj, void *_result) {
104135
105136 for (i = 0 ; i < len ; i ++ ) {
106137 PyObject * item ;
107- #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
108- int r ;
109- PyObject * bytes ;
110- #endif
111- char * s , * s2 ;
138+ _cleanup_Py_DECREF_ PyObject * bytes = NULL ;
139+ char * s ;
112140
113141 item = PySequence_ITEM (obj , i );
114- #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
115- r = PyUnicode_FSConverter (item , & bytes );
116- if (r == 0 )
117- goto cleanup ;
118-
119- s = PyBytes_AsString (bytes );
120- #else
121- s = PyString_AsString (item );
122- #endif
142+ s = convert_path (item , & bytes );
123143 if (!s )
124144 goto cleanup ;
125145
126- s2 = strdup (s );
127- if (!s2 ) {
146+ s = strdup (s );
147+ if (!s ) {
128148 set_error (- ENOMEM , NULL , NULL );
129149 goto cleanup ;
130150 }
131151
132- (* result )[i ] = s2 ;
152+ (* result )[i ] = s ;
133153 }
134154
135155 return 1 ;
@@ -151,49 +171,84 @@ PyDoc_STRVAR(Reader__doc__,
151171 "_Reader allows filtering and retrieval of Journal entries.\n"
152172 "Note: this is a low-level interface, and probably not what you\n"
153173 "want, use systemd.journal.Reader instead.\n\n"
154- "Argument `flags` sets open flags of the journal, which can be one\n"
155- "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
156- "journal on local machine only; RUNTIME_ONLY opens only\n"
157- "volatile journal files; and SYSTEM opens journal files of\n"
158- "system services and the kernel, and CURRENT_USER opens files\n"
159- "of the current user.\n\n"
160- "Argument `path` is the directory of journal files.\n"
161- "Argument `files` is a list of files. Note that\n"
162- "`flags`, `path`, and `files` are exclusive.\n\n"
163- "_Reader implements the context manager protocol: the journal\n"
164- "will be closed when exiting the block." );
174+ "Argument `flags` sets open flags of the journal, which can be one of, or an ORed\n"
175+ "combination of constants: LOCAL_ONLY (default) opens journal on local machine only;\n"
176+ "RUNTIME_ONLY opens only volatile journal files; and SYSTEM opens journal files of\n"
177+ "system services and the kernel, and CURRENT_USER opens file of the current user.\n"
178+ "\n"
179+ "Instead of opening the system journal, argument `path` may specify a directory\n"
180+ "which contains the journal. It maybe be either a file system path (a string), or\n"
181+ "a file descriptor (an integer). Alternatively, argument `files` may specify a list\n"
182+ "of journal file names. Note that `flags`, `path`, `files`, `directory_fd` are\n"
183+ "exclusive.\n\n"
184+ "_Reader implements the context manager protocol: the journal will be closed when\n"
185+ "exiting the block." );
165186static int Reader_init (Reader * self , PyObject * args , PyObject * keywds ) {
166187 int flags = 0 , r ;
167- char * path = NULL ;
168- char * * files = NULL ;
188+ PyObject * _path = NULL ;
189+ _cleanup_strv_free_ char * * files = NULL ;
169190
170191 static const char * const kwlist [] = {"flags" , "path" , "files" , NULL };
171- if (!PyArg_ParseTupleAndKeywords (args , keywds , "|izO&:__init__" , (char * * ) kwlist ,
172- & flags , & path , strv_converter , & files ))
192+ if (!PyArg_ParseTupleAndKeywords (args , keywds , "|iO&O&:__init__" , (char * * ) kwlist ,
193+ & flags ,
194+ null_converter , & _path ,
195+ strv_converter , & files ))
173196 return -1 ;
174197
175- if (!!flags + !!path + !!files > 1 ) {
176- PyErr_SetString (PyExc_ValueError , "cannot use more than one of flags, path, and files" );
198+ if (!!flags + !!_path + !!files > 1 ) {
199+ PyErr_SetString (PyExc_ValueError ,
200+ "cannot use more than one of flags, path, and files" );
177201 return -1 ;
178202 }
179203
180- if (!flags )
181- flags = SD_JOURNAL_LOCAL_ONLY ;
204+ if (_path ) {
205+ if (long_Check (_path )) {
206+ long directory_fd = long_AsLong (_path );
207+ if (PyErr_Occurred ())
208+ return -1 ;
182209
183- Py_BEGIN_ALLOW_THREADS
184- if (path )
185- r = sd_journal_open_directory (& self -> j , path , 0 );
186- else if (files ) {
210+ if ((int ) directory_fd != directory_fd ) {
211+ PyErr_SetString (PyExc_OverflowError , "Value too large" );
212+ return -1 ;
213+ }
214+
215+ #ifdef HAVE_JOURNAL_OPEN_DIRECTORY_FD
216+ Py_BEGIN_ALLOW_THREADS
217+ r = sd_journal_open_directory_fd (& self -> j , (int ) directory_fd , 0 );
218+ Py_END_ALLOW_THREADS
219+ #else
220+ r = - ENOSYS ;
221+ #endif
222+ } else {
223+ char * path = NULL ;
224+ _cleanup_Py_DECREF_ PyObject * path_bytes = NULL ;
225+
226+ path = convert_path (_path , & path_bytes );
227+ if (!path )
228+ return -1 ;
229+
230+ Py_BEGIN_ALLOW_THREADS
231+ r = sd_journal_open_directory (& self -> j , path , 0 );
232+ Py_END_ALLOW_THREADS
233+ }
234+ } else if (files ) {
187235#ifdef HAVE_JOURNAL_OPEN_FILES
236+ Py_BEGIN_ALLOW_THREADS
188237 r = sd_journal_open_files (& self -> j , (const char * * ) files , 0 );
238+ Py_END_ALLOW_THREADS
189239#else
190240 r = - ENOSYS ;
191241#endif
192- } else
242+ } else {
243+ if (!flags )
244+ flags = SD_JOURNAL_LOCAL_ONLY ;
245+
246+ Py_BEGIN_ALLOW_THREADS
193247 r = sd_journal_open (& self -> j , flags );
194- Py_END_ALLOW_THREADS
248+ Py_END_ALLOW_THREADS
249+ }
195250
196- return set_error (r , path , "Invalid flags or path " );
251+ return set_error (r , NULL , "Opening the journal failed " );
197252}
198253
199254PyDoc_STRVAR (Reader_fileno__doc__ ,
0 commit comments