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 /*  only one instance, only one syslog, so globals should be ok  */
58 static PyObject *S_ident_o = NULL;                      /*  identifier, held by openlog()  */
59 static char S_log_open = 0;
60 
61 
62 static PyObject *
syslog_get_argv(void)63 syslog_get_argv(void)
64 {
65     /* Figure out what to use for as the program "ident" for openlog().
66      * This swallows exceptions and continues rather than failing out,
67      * because the syslog module can still be used because openlog(3)
68      * is optional.
69      */
70 
71     Py_ssize_t argv_len, scriptlen;
72     PyObject *scriptobj;
73     Py_ssize_t slash;
74     PyObject *argv = PySys_GetObject("argv");
75 
76     if (argv == NULL) {
77         return(NULL);
78     }
79 
80     argv_len = PyList_Size(argv);
81     if (argv_len == -1) {
82         PyErr_Clear();
83         return(NULL);
84     }
85     if (argv_len == 0) {
86         return(NULL);
87     }
88 
89     scriptobj = PyList_GetItem(argv, 0);
90     if (scriptobj == NULL) {
91         PyErr_Clear();
92         return NULL;
93     }
94     if (!PyUnicode_Check(scriptobj)) {
95         return(NULL);
96     }
97     scriptlen = PyUnicode_GET_LENGTH(scriptobj);
98     if (scriptlen == 0) {
99         return(NULL);
100     }
101 
102     slash = PyUnicode_FindChar(scriptobj, SEP, 0, scriptlen, -1);
103     if (slash == -2) {
104         PyErr_Clear();
105         return NULL;
106     }
107     if (slash != -1) {
108         return PyUnicode_Substring(scriptobj, slash + 1, scriptlen);
109     } else {
110         Py_INCREF(scriptobj);
111         return(scriptobj);
112     }
113 }
114 
115 
116 static PyObject *
syslog_openlog(PyObject * self,PyObject * args,PyObject * kwds)117 syslog_openlog(PyObject * self, PyObject * args, PyObject *kwds)
118 {
119     long logopt = 0;
120     long facility = LOG_USER;
121     PyObject *ident = NULL;
122     static char *keywords[] = {"ident", "logoption", "facility", 0};
123     const char *ident_str = NULL;
124 
125     if (!PyArg_ParseTupleAndKeywords(args, kwds,
126                           "|Ull:openlog", keywords, &ident, &logopt, &facility))
127         return NULL;
128 
129     if (ident) {
130         Py_INCREF(ident);
131     }
132     else {
133         /* get sys.argv[0] or NULL if we can't for some reason  */
134         ident = syslog_get_argv();
135     }
136 
137     /* At this point, ident should be INCREF()ed.  openlog(3) does not
138      * make a copy, and syslog(3) later uses it.  We can't garbagecollect it.
139      * If NULL, just let openlog figure it out (probably using C argv[0]).
140      */
141     if (ident) {
142         ident_str = PyUnicode_AsUTF8(ident);
143         if (ident_str == NULL) {
144             Py_DECREF(ident);
145             return NULL;
146         }
147     }
148     if (PySys_Audit("syslog.openlog", "Oll", ident ? ident : Py_None, logopt, facility) < 0) {
149         Py_DECREF(ident);
150         return NULL;
151     }
152 
153     openlog(ident_str, logopt, facility);
154     S_log_open = 1;
155     Py_XSETREF(S_ident_o, ident);
156 
157     Py_RETURN_NONE;
158 }
159 
160 
161 static PyObject *
syslog_syslog(PyObject * self,PyObject * args)162 syslog_syslog(PyObject * self, PyObject * args)
163 {
164     PyObject *message_object;
165     const char *message;
166     int   priority = LOG_INFO;
167 
168     if (!PyArg_ParseTuple(args, "iU;[priority,] message string",
169                           &priority, &message_object)) {
170         PyErr_Clear();
171         if (!PyArg_ParseTuple(args, "U;[priority,] message string",
172                               &message_object))
173             return NULL;
174     }
175 
176     message = PyUnicode_AsUTF8(message_object);
177     if (message == NULL)
178         return NULL;
179 
180     if (PySys_Audit("syslog.syslog", "is", priority, message) < 0) {
181         return NULL;
182     }
183 
184     /*  if log is not opened, open it now  */
185     if (!S_log_open) {
186         PyObject *openargs;
187 
188         /* Continue even if PyTuple_New fails, because openlog(3) is optional.
189          * So, we can still do logging in the unlikely event things are so hosed
190          * that we can't do this tuple.
191          */
192         if ((openargs = PyTuple_New(0))) {
193             PyObject *openlog_ret = syslog_openlog(self, openargs, NULL);
194             Py_DECREF(openargs);
195             if (openlog_ret == NULL) {
196                 return NULL;
197             }
198             Py_DECREF(openlog_ret);
199         }
200         else {
201             return NULL;
202         }
203     }
204 
205     /* Incref ident, because it can be decrefed if syslog.openlog() is
206      * called when the GIL is released.
207      */
208     PyObject *ident = S_ident_o;
209     Py_XINCREF(ident);
210 #ifdef __APPLE__
211     // gh-98178: On macOS, libc syslog() is not thread-safe
212     syslog(priority, "%s", message);
213 #else
214     Py_BEGIN_ALLOW_THREADS;
215     syslog(priority, "%s", message);
216     Py_END_ALLOW_THREADS;
217 #endif
218     Py_XDECREF(ident);
219     Py_RETURN_NONE;
220 }
221 
222 static PyObject *
syslog_closelog(PyObject * self,PyObject * unused)223 syslog_closelog(PyObject *self, PyObject *unused)
224 {
225     if (PySys_Audit("syslog.closelog", NULL) < 0) {
226         return NULL;
227     }
228     if (S_log_open) {
229         closelog();
230         Py_CLEAR(S_ident_o);
231         S_log_open = 0;
232     }
233     Py_RETURN_NONE;
234 }
235 
236 static PyObject *
syslog_setlogmask(PyObject * self,PyObject * args)237 syslog_setlogmask(PyObject *self, PyObject *args)
238 {
239     long maskpri, omaskpri;
240 
241     if (!PyArg_ParseTuple(args, "l;mask for priority", &maskpri))
242         return NULL;
243     if (PySys_Audit("syslog.setlogmask", "l", maskpri) < 0) {
244         return NULL;
245     }
246     omaskpri = setlogmask(maskpri);
247     return PyLong_FromLong(omaskpri);
248 }
249 
250 static PyObject *
syslog_log_mask(PyObject * self,PyObject * args)251 syslog_log_mask(PyObject *self, PyObject *args)
252 {
253     long mask;
254     long pri;
255     if (!PyArg_ParseTuple(args, "l:LOG_MASK", &pri))
256         return NULL;
257     mask = LOG_MASK(pri);
258     return PyLong_FromLong(mask);
259 }
260 
261 static PyObject *
syslog_log_upto(PyObject * self,PyObject * args)262 syslog_log_upto(PyObject *self, PyObject *args)
263 {
264     long mask;
265     long pri;
266     if (!PyArg_ParseTuple(args, "l:LOG_UPTO", &pri))
267         return NULL;
268     mask = LOG_UPTO(pri);
269     return PyLong_FromLong(mask);
270 }
271 
272 /* List of functions defined in the module */
273 
274 static PyMethodDef syslog_methods[] = {
275     {"openlog",         _PyCFunction_CAST(syslog_openlog),           METH_VARARGS | METH_KEYWORDS},
276     {"closelog",        syslog_closelog,        METH_NOARGS},
277     {"syslog",          syslog_syslog,          METH_VARARGS},
278     {"setlogmask",      syslog_setlogmask,      METH_VARARGS},
279     {"LOG_MASK",        syslog_log_mask,        METH_VARARGS},
280     {"LOG_UPTO",        syslog_log_upto,        METH_VARARGS},
281     {NULL,              NULL,                   0}
282 };
283 
284 
285 static int
syslog_exec(PyObject * module)286 syslog_exec(PyObject *module)
287 {
288 #define ADD_INT_MACRO(module, macro)                                  \
289     do {                                                              \
290         if (PyModule_AddIntConstant(module, #macro, macro) < 0) {     \
291             return -1;                                                \
292         }                                                             \
293     } while (0)
294     /* Priorities */
295     ADD_INT_MACRO(module, LOG_EMERG);
296     ADD_INT_MACRO(module, LOG_ALERT);
297     ADD_INT_MACRO(module, LOG_CRIT);
298     ADD_INT_MACRO(module, LOG_ERR);
299     ADD_INT_MACRO(module, LOG_WARNING);
300     ADD_INT_MACRO(module, LOG_NOTICE);
301     ADD_INT_MACRO(module, LOG_INFO);
302     ADD_INT_MACRO(module, LOG_DEBUG);
303 
304     /* openlog() option flags */
305     ADD_INT_MACRO(module, LOG_PID);
306     ADD_INT_MACRO(module, LOG_CONS);
307     ADD_INT_MACRO(module, LOG_NDELAY);
308 #ifdef LOG_ODELAY
309     ADD_INT_MACRO(module, LOG_ODELAY);
310 #endif
311 #ifdef LOG_NOWAIT
312     ADD_INT_MACRO(module, LOG_NOWAIT);
313 #endif
314 #ifdef LOG_PERROR
315     ADD_INT_MACRO(module, LOG_PERROR);
316 #endif
317 
318     /* Facilities */
319     ADD_INT_MACRO(module, LOG_KERN);
320     ADD_INT_MACRO(module, LOG_USER);
321     ADD_INT_MACRO(module, LOG_MAIL);
322     ADD_INT_MACRO(module, LOG_DAEMON);
323     ADD_INT_MACRO(module, LOG_AUTH);
324     ADD_INT_MACRO(module, LOG_LPR);
325     ADD_INT_MACRO(module, LOG_LOCAL0);
326     ADD_INT_MACRO(module, LOG_LOCAL1);
327     ADD_INT_MACRO(module, LOG_LOCAL2);
328     ADD_INT_MACRO(module, LOG_LOCAL3);
329     ADD_INT_MACRO(module, LOG_LOCAL4);
330     ADD_INT_MACRO(module, LOG_LOCAL5);
331     ADD_INT_MACRO(module, LOG_LOCAL6);
332     ADD_INT_MACRO(module, LOG_LOCAL7);
333 
334 #ifndef LOG_SYSLOG
335 #define LOG_SYSLOG              LOG_DAEMON
336 #endif
337 #ifndef LOG_NEWS
338 #define LOG_NEWS                LOG_MAIL
339 #endif
340 #ifndef LOG_UUCP
341 #define LOG_UUCP                LOG_MAIL
342 #endif
343 #ifndef LOG_CRON
344 #define LOG_CRON                LOG_DAEMON
345 #endif
346 
347     ADD_INT_MACRO(module, LOG_SYSLOG);
348     ADD_INT_MACRO(module, LOG_CRON);
349     ADD_INT_MACRO(module, LOG_UUCP);
350     ADD_INT_MACRO(module, LOG_NEWS);
351 
352 #ifdef LOG_AUTHPRIV
353     ADD_INT_MACRO(module, LOG_AUTHPRIV);
354 #endif
355 
356     return 0;
357 }
358 
359 static PyModuleDef_Slot syslog_slots[] = {
360     {Py_mod_exec, syslog_exec},
361     {0, NULL}
362 };
363 
364 /* Initialization function for the module */
365 
366 static struct PyModuleDef syslogmodule = {
367     PyModuleDef_HEAD_INIT,
368     .m_name = "syslog",
369     .m_size = 0,
370     .m_methods = syslog_methods,
371     .m_slots = syslog_slots,
372 };
373 
374 PyMODINIT_FUNC
PyInit_syslog(void)375 PyInit_syslog(void)
376 {
377     return PyModuleDef_Init(&syslogmodule);
378 }
379