1 /***********************************************************
2 Copyright 1994 by Lance Ellinghouse,
3 Cathedral City, California Republic, United States of America.
4
5 All Rights Reserved
6
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that the name of Lance Ellinghouse
12 not be used in advertising or publicity pertaining to distribution
13 of the software without specific, written prior permission.
14
15 LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
16 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE BE LIABLE FOR ANY SPECIAL,
18 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
19 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23 ******************************************************************/
24
25 /******************************************************************
26
27 Revision history:
28
29 2010/04/20 (Sean Reifschneider)
30 - Use basename(sys.argv[0]) for the default "ident".
31 - Arguments to openlog() are now keyword args and are all optional.
32 - syslog() calls openlog() if it hasn't already been called.
33
34 1998/04/28 (Sean Reifschneider)
35 - When facility not specified to syslog() method, use default from openlog()
36 (This is how it was claimed to work in the documentation)
37 - Potential resource leak of o_ident, now cleaned up in closelog()
38 - Minor comment accuracy fix.
39
40 95/06/29 (Steve Clift)
41 - Changed arg parsing to use PyArg_ParseTuple.
42 - Added PyErr_Clear() call(s) where needed.
43 - Fix core dumps if user message contains format specifiers.
44 - Change openlog arg defaults to match normal syslog behavior.
45 - Plug memory leak in openlog().
46 - Fix setlogmask() to return previous mask value.
47
48 ******************************************************************/
49
50 /* syslog module */
51
52 #include "Python.h"
53 #include "osdefs.h" // SEP
54
55 #include <syslog.h>
56
57 /*[clinic input]
58 module syslog
59 [clinic start generated code]*/
60 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=478f4ac94a1d4cae]*/
61
62 #include "clinic/syslogmodule.c.h"
63
64 /* only one instance, only one syslog, so globals should be ok,
65 * these fields are writable from the main interpreter only. */
66 static PyObject *S_ident_o = NULL; // identifier, held by openlog()
67 static char S_log_open = 0;
68
69 static inline int
70 is_main_interpreter(void)
71 {
72 return (PyInterpreterState_Get() == PyInterpreterState_Main());
73 }
74
75 static PyObject *
76 syslog_get_argv(void)
77 {
78 /* Figure out what to use for as the program "ident" for openlog().
79 * This swallows exceptions and continues rather than failing out,
80 * because the syslog module can still be used because openlog(3)
81 * is optional.
82 */
83
84 Py_ssize_t argv_len, scriptlen;
85 PyObject *scriptobj;
86 Py_ssize_t slash;
87 PyObject *argv = PySys_GetObject("argv");
88
89 if (argv == NULL) {
90 return(NULL);
91 }
92
93 argv_len = PyList_Size(argv);
94 if (argv_len == -1) {
95 PyErr_Clear();
96 return(NULL);
97 }
98 if (argv_len == 0) {
99 return(NULL);
100 }
101
102 scriptobj = PyList_GetItem(argv, 0);
103 if (scriptobj == NULL) {
104 PyErr_Clear();
105 return NULL;
106 }
107 if (!PyUnicode_Check(scriptobj)) {
108 return(NULL);
109 }
110 scriptlen = PyUnicode_GET_LENGTH(scriptobj);
111 if (scriptlen == 0) {
112 return(NULL);
113 }
114
115 slash = PyUnicode_FindChar(scriptobj, SEP, 0, scriptlen, -1);
116 if (slash == -2) {
117 PyErr_Clear();
118 return NULL;
119 }
120 if (slash != -1) {
121 return PyUnicode_Substring(scriptobj, slash + 1, scriptlen);
122 } else {
123 Py_INCREF(scriptobj);
124 return(scriptobj);
125 }
126 }
127
128
129 /*[clinic input]
130 syslog.openlog
131
132 ident: unicode = NULL
133 logoption as logopt: long = 0
134 facility: long(c_default="LOG_USER") = LOG_USER
135
136 Set logging options of subsequent syslog() calls.
137 [clinic start generated code]*/
138
139 static PyObject *
140 syslog_openlog_impl(PyObject *module, PyObject *ident, long logopt,
141 long facility)
142 /*[clinic end generated code: output=5476c12829b6eb75 input=8a987a96a586eee7]*/
143 {
144 // Since the sys.openlog changes the process level state of syslog library,
145 // this operation is only allowed for the main interpreter.
146 if (!is_main_interpreter()) {
147 PyErr_SetString(PyExc_RuntimeError, "subinterpreter can't use syslog.openlog()");
148 return NULL;
149 }
150
151 const char *ident_str = NULL;
152
153 if (ident) {
154 Py_INCREF(ident);
155 }
156 else {
157 /* get sys.argv[0] or NULL if we can't for some reason */
158 ident = syslog_get_argv();
159 }
160
161 /* At this point, ident should be INCREF()ed. openlog(3) does not
162 * make a copy, and syslog(3) later uses it. We can't garbagecollect it.
163 * If NULL, just let openlog figure it out (probably using C argv[0]).
164 */
165 if (ident) {
166 ident_str = PyUnicode_AsUTF8(ident);
167 if (ident_str == NULL) {
168 Py_DECREF(ident);
169 return NULL;
170 }
171 }
172 if (PySys_Audit("syslog.openlog", "Oll", ident ? ident : Py_None, logopt, facility) < 0) {
173 Py_DECREF(ident);
174 return NULL;
175 }
176
177 openlog(ident_str, logopt, facility);
178 S_log_open = 1;
179 Py_XSETREF(S_ident_o, ident);
180
181 Py_RETURN_NONE;
182 }
183
184
185
186 /*[clinic input]
187 syslog.syslog
188
189 [
190 priority: int(c_default="LOG_INFO") = LOG_INFO
191 ]
192
193 message: str
194
195 /
196
197 Send the string message to the system logger.
198 [clinic start generated code]*/
199
200 static PyObject *
201 syslog_syslog_impl(PyObject *module, int group_left_1, int priority,
202 const char *message)
203 /*[clinic end generated code: output=c3dbc73445a0e078 input=ac83d92b12ea3d4e]*/
204 {
205 if (PySys_Audit("syslog.syslog", "is", priority, message) < 0) {
206 return NULL;
207 }
208
209 /* if log is not opened, open it now */
210 if (!S_log_open) {
211 if (!is_main_interpreter()) {
212 PyErr_SetString(PyExc_RuntimeError, "subinterpreter can't use syslog.syslog() "
213 "until the syslog is opened by the main interpreter");
214 return NULL;
215 }
216 PyObject *openlog_ret = syslog_openlog_impl(module, NULL, 0, LOG_USER);
217 if (openlog_ret == NULL) {
218 return NULL;
219 }
220 Py_DECREF(openlog_ret);
221 }
222
223 /* Incref ident, because it can be decrefed if syslog.openlog() is
224 * called when the GIL is released.
225 */
226 PyObject *ident = Py_XNewRef(S_ident_o);
227 #ifdef __APPLE__
228 // gh-98178: On macOS, libc syslog() is not thread-safe
229 syslog(priority, "%s", message);
230 #else
231 Py_BEGIN_ALLOW_THREADS;
232 syslog(priority, "%s", message);
233 Py_END_ALLOW_THREADS;
234 #endif
235 Py_XDECREF(ident);
236 Py_RETURN_NONE;
237 }
238
239
240 /*[clinic input]
241 syslog.closelog
242
243 Reset the syslog module values and call the system library closelog().
244 [clinic start generated code]*/
245
246 static PyObject *
247 syslog_closelog_impl(PyObject *module)
248 /*[clinic end generated code: output=97890a80a24b1b84 input=fb77a54d447acf07]*/
249 {
250 // Since the sys.closelog changes the process level state of syslog library,
251 // this operation is only allowed for the main interpreter.
252 if (!is_main_interpreter()) {
253 PyErr_SetString(PyExc_RuntimeError, "sunbinterpreter can't use syslog.closelog()");
254 return NULL;
255 }
256
257 if (PySys_Audit("syslog.closelog", NULL) < 0) {
258 return NULL;
259 }
260 if (S_log_open) {
261 closelog();
262 Py_CLEAR(S_ident_o);
263 S_log_open = 0;
264 }
265 Py_RETURN_NONE;
266 }
267
268 /*[clinic input]
269 syslog.setlogmask -> long
270
271 maskpri: long
272 /
273
274 Set the priority mask to maskpri and return the previous mask value.
275 [clinic start generated code]*/
276
277 static long
278 syslog_setlogmask_impl(PyObject *module, long maskpri)
279 /*[clinic end generated code: output=d6ed163917b434bf input=adff2c2b76c7629c]*/
280 {
281 if (PySys_Audit("syslog.setlogmask", "l", maskpri) < 0) {
282 return -1;
283 }
284
285 return setlogmask(maskpri);
286 }
287
288 /*[clinic input]
289 syslog.LOG_MASK -> long
290
291 pri: long
292 /
293
294 Calculates the mask for the individual priority pri.
295 [clinic start generated code]*/
296
297 static long
298 syslog_LOG_MASK_impl(PyObject *module, long pri)
299 /*[clinic end generated code: output=c4a5bbfcc74c7c94 input=534829cb7fb5f7d2]*/
300 {
301 return LOG_MASK(pri);
302 }
303
304 /*[clinic input]
305 syslog.LOG_UPTO -> long
306
307 pri: long
308 /
309
310 Calculates the mask for all priorities up to and including pri.
311 [clinic start generated code]*/
312
313 static long
314 syslog_LOG_UPTO_impl(PyObject *module, long pri)
315 /*[clinic end generated code: output=9eab083c90601d7e input=5e906d6c406b7458]*/
316 {
317 return LOG_UPTO(pri);
318 }
319
320 /* List of functions defined in the module */
321
322 static PyMethodDef syslog_methods[] = {
323 SYSLOG_OPENLOG_METHODDEF
324 SYSLOG_CLOSELOG_METHODDEF
325 SYSLOG_SYSLOG_METHODDEF
326 SYSLOG_SETLOGMASK_METHODDEF
327 SYSLOG_LOG_MASK_METHODDEF
328 SYSLOG_LOG_UPTO_METHODDEF
329 {NULL, NULL, 0}
330 };
331
332
333 static int
334 syslog_exec(PyObject *module)
335 {
336 #define ADD_INT_MACRO(module, macro) \
337 do { \
338 if (PyModule_AddIntConstant(module, #macro, macro) < 0) { \
339 return -1; \
340 } \
341 } while (0)
342 /* Priorities */
343 ADD_INT_MACRO(module, LOG_EMERG);
344 ADD_INT_MACRO(module, LOG_ALERT);
345 ADD_INT_MACRO(module, LOG_CRIT);
346 ADD_INT_MACRO(module, LOG_ERR);
347 ADD_INT_MACRO(module, LOG_WARNING);
348 ADD_INT_MACRO(module, LOG_NOTICE);
349 ADD_INT_MACRO(module, LOG_INFO);
350 ADD_INT_MACRO(module, LOG_DEBUG);
351
352 /* openlog() option flags */
353 ADD_INT_MACRO(module, LOG_PID);
354 ADD_INT_MACRO(module, LOG_CONS);
355 ADD_INT_MACRO(module, LOG_NDELAY);
356 #ifdef LOG_ODELAY
357 ADD_INT_MACRO(module, LOG_ODELAY);
358 #endif
359 #ifdef LOG_NOWAIT
360 ADD_INT_MACRO(module, LOG_NOWAIT);
361 #endif
362 #ifdef LOG_PERROR
363 ADD_INT_MACRO(module, LOG_PERROR);
364 #endif
365
366 /* Facilities */
367 ADD_INT_MACRO(module, LOG_KERN);
368 ADD_INT_MACRO(module, LOG_USER);
369 ADD_INT_MACRO(module, LOG_MAIL);
370 ADD_INT_MACRO(module, LOG_DAEMON);
371 ADD_INT_MACRO(module, LOG_AUTH);
372 ADD_INT_MACRO(module, LOG_LPR);
373 ADD_INT_MACRO(module, LOG_LOCAL0);
374 ADD_INT_MACRO(module, LOG_LOCAL1);
375 ADD_INT_MACRO(module, LOG_LOCAL2);
376 ADD_INT_MACRO(module, LOG_LOCAL3);
377 ADD_INT_MACRO(module, LOG_LOCAL4);
378 ADD_INT_MACRO(module, LOG_LOCAL5);
379 ADD_INT_MACRO(module, LOG_LOCAL6);
380 ADD_INT_MACRO(module, LOG_LOCAL7);
381
382 #ifndef LOG_SYSLOG
383 #define LOG_SYSLOG LOG_DAEMON
384 #endif
385 #ifndef LOG_NEWS
386 #define LOG_NEWS LOG_MAIL
387 #endif
388 #ifndef LOG_UUCP
389 #define LOG_UUCP LOG_MAIL
390 #endif
391 #ifndef LOG_CRON
392 #define LOG_CRON LOG_DAEMON
393 #endif
394
395 ADD_INT_MACRO(module, LOG_SYSLOG);
396 ADD_INT_MACRO(module, LOG_CRON);
397 ADD_INT_MACRO(module, LOG_UUCP);
398 ADD_INT_MACRO(module, LOG_NEWS);
399
400 #ifdef LOG_AUTHPRIV
401 ADD_INT_MACRO(module, LOG_AUTHPRIV);
402 #endif
403
404 return 0;
405 }
406
407 static PyModuleDef_Slot syslog_slots[] = {
408 {Py_mod_exec, syslog_exec},
409 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
410 {0, NULL}
411 };
412
413 /* Initialization function for the module */
414
415 static struct PyModuleDef syslogmodule = {
416 PyModuleDef_HEAD_INIT,
417 .m_name = "syslog",
418 .m_size = 0,
419 .m_methods = syslog_methods,
420 .m_slots = syslog_slots,
421 };
422
423 PyMODINIT_FUNC
424 PyInit_syslog(void)
425 {
426 return PyModuleDef_Init(&syslogmodule);
427 }