Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/plugins/python/python_loghandler.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2020 Robert Manner <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include "sudo_python_module.h"
20
21
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9
22
# define PyObject_CallNoArgs(_o) PyObject_CallObject((_o), NULL)
23
#endif
24
25
static void
26
_debug_plugin(unsigned int log_level, const char *log_message)
27
{
28
debug_decl_vars(python_sudo_debug, PYTHON_DEBUG_PLUGIN);
29
30
if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
31
// at trace level we output the position for the python log as well
32
char *func_name = NULL, *file_name = NULL;
33
long line_number = -1;
34
35
if (py_get_current_execution_frame(&file_name, &line_number, &func_name) == SUDO_RC_OK) {
36
sudo_debug_printf(SUDO_DEBUG_INFO, "%s @ %s:%ld debugs:\n",
37
func_name, file_name, line_number);
38
}
39
40
free(func_name);
41
free(file_name);
42
}
43
44
sudo_debug_printf(log_level, "%s\n", log_message);
45
}
46
47
PyObject *
48
python_sudo_debug(PyObject *Py_UNUSED(py_self), PyObject *py_args)
49
{
50
debug_decl(python_sudo_debug, PYTHON_DEBUG_C_CALLS);
51
py_debug_python_call("sudo", "debug", py_args, NULL, PYTHON_DEBUG_C_CALLS);
52
53
unsigned int log_level = SUDO_DEBUG_DEBUG;
54
const char *log_message = NULL;
55
if (!PyArg_ParseTuple(py_args, "is:sudo.debug", &log_level, &log_message)) {
56
debug_return_ptr(NULL);
57
}
58
59
_debug_plugin(log_level, log_message);
60
61
debug_return_ptr_pynone;
62
}
63
64
static unsigned int
65
_sudo_log_level_from_python(long level)
66
{
67
if (level >= 50)
68
return SUDO_DEBUG_CRIT;
69
if (level >= 40)
70
return SUDO_DEBUG_ERROR;
71
if (level >= 30)
72
return SUDO_DEBUG_WARN;
73
if (level >= 20)
74
return SUDO_DEBUG_INFO;
75
76
return SUDO_DEBUG_TRACE;
77
}
78
79
static PyObject *
80
_sudo_LogHandler__emit(PyObject *py_self, PyObject *py_args)
81
{
82
debug_decl(_sudo_LogHandler__emit, PYTHON_DEBUG_C_CALLS);
83
84
PyObject *py_record = NULL; // borrowed
85
PyObject *py_message = NULL;
86
87
py_debug_python_call("LogHandler", "emit", py_args, NULL, PYTHON_DEBUG_C_CALLS);
88
89
if (!PyArg_UnpackTuple(py_args, "sudo.LogHandler.emit", 2, 2, &py_self, &py_record))
90
goto cleanup;
91
92
long python_loglevel = py_object_get_optional_attr_number(py_record, "levelno");
93
if (PyErr_Occurred()) {
94
PyErr_Format(sudo_exc_SudoException, "sudo.LogHandler: Failed to determine log level");
95
goto cleanup;
96
}
97
98
unsigned int sudo_loglevel = _sudo_log_level_from_python(python_loglevel);
99
100
py_message = PyObject_CallMethod(py_self, "format", "O", py_record);
101
if (py_message == NULL)
102
goto cleanup;
103
104
_debug_plugin(sudo_loglevel, PyUnicode_AsUTF8(py_message));
105
106
cleanup:
107
Py_CLEAR(py_message);
108
if (PyErr_Occurred()) {
109
debug_return_ptr(NULL);
110
}
111
112
debug_return_ptr_pynone;
113
}
114
115
/* The sudo.LogHandler class can be used to make the default python logger
116
* use sudo's built in log system. */
117
static PyMethodDef _sudo_LogHandler_class_methods[] =
118
{
119
{"emit", _sudo_LogHandler__emit, METH_VARARGS, ""},
120
{NULL, NULL, 0, NULL}
121
};
122
123
// This function creates the sudo.LogHandler class and adds it
124
// to the root logger.
125
int
126
sudo_module_set_default_loghandler()
127
{
128
debug_decl(sudo_module_set_default_loghandler, PYTHON_DEBUG_INTERNAL);
129
130
PyObject *py_sudo, *py_logging_module = NULL, *py_logger = NULL,
131
*py_streamhandler = NULL, *py_class = NULL,
132
*py_loghandler = NULL, *py_result = NULL;
133
134
py_sudo = PyImport_ImportModule("sudo");
135
if (py_sudo == NULL)
136
goto cleanup;
137
138
py_logging_module = PyImport_ImportModule("logging");
139
if (py_logging_module == NULL)
140
goto cleanup;
141
142
// Get the root logger which all loggers descend from.
143
py_logger = PyObject_CallMethod(py_logging_module, "getLogger", NULL);
144
if (py_logger == NULL)
145
goto cleanup;
146
147
py_streamhandler = PyObject_GetAttrString(py_logging_module, "StreamHandler");
148
if (py_streamhandler == NULL)
149
goto cleanup;
150
151
// Create our own handler that is a sub-class of StreamHandler
152
py_class = sudo_module_create_class("sudo.LogHandler",
153
_sudo_LogHandler_class_methods, py_streamhandler);
154
if (py_class == NULL)
155
goto cleanup;
156
157
// PyModule_AddObject steals a reference to py_class on success
158
if (PyModule_AddObject(py_sudo, "LogHandler", py_class) < 0)
159
goto cleanup;
160
Py_INCREF(py_class);
161
162
py_loghandler = PyObject_CallNoArgs(py_class);
163
if (py_loghandler == NULL)
164
goto cleanup;
165
166
py_result = PyObject_CallMethod(py_logger, "addHandler", "O", py_loghandler);
167
168
cleanup:
169
Py_CLEAR(py_result);
170
Py_CLEAR(py_loghandler);
171
Py_CLEAR(py_class);
172
Py_CLEAR(py_streamhandler);
173
Py_CLEAR(py_logger);
174
Py_CLEAR(py_logging_module);
175
Py_CLEAR(py_sudo);
176
debug_return_int(PyErr_Occurred() ? SUDO_RC_ERROR : SUDO_RC_OK);
177
}
178
179