Dictionary order is not stable in Python < 3.6 so we need to sort
by key to have consistent results.
The LogHandler output is also different on older Python versions.
Also, don't stop running python tests after the first error.
so python log system can be used with sudo logsystem.
Loggers use it by default (the handler is set on the root logger).
If that is not the intent, it can be overridden explicitly.
If "ClassName" is not specified, load the one and only sudo.Plugin from
the module (if so), otherwise display which plugins are available from
which the system admin can choose.
This only happens when sudo unloads the last python plugin.
The reason doing so is because there are some python modules which
does not support importing them again after destroying the interpreter
which has imported them previously.
Another solution would be to just leak the interpreters (let the kernel
free up), but then there might be some python resources like open files
would not get cleaned up correctly if the plugin is badly written.
Tests are meant to test the scenario sudo does, so I have modified them
to generally do not unlink but only a few times (~per plugin type) so it
does not use 48 interpreters (one gets started on every plugin->open) and
it is visible at least which type of plugin fails deinit if there is an
error.
even if the arguments are not used (eg. when there is no "close" call
in the plugin).
It was not really a memleak, because interpreter is deinitialized anyway,
which frees the object.
The error is always stored in plugin_ctx, but it is only set into errstr
if the API version is enough. (Previously it worked the opposite:
we only stored the error if API level was enough.)
Plugins can raise a sudo.PluginError exception to add context message
for the failure.
The callback's errstr gets filled up with the specified message.
But, as sudo expects a string constant (will not free the string),
we store it in the plugin context at least until next callback
invocation.
It is a bit more code, but it is more "pythonic" and easier to debug
as the enum values also know their names.
It is also an API break, eg. sudo.RC_OK becomes sudo.RC.OK as sudo.RC will
be the "type" of the enum, but I guess that is acceptable before the
initial release.
On each plugin initialization we create a separate python interpreter
which gets stored in the plugin_ctx. The main interpreter is
stored in py_ctx and is used for creating more interpreters (if more plugins
get loaded) and final python deinitialization.
The "traceback" module import and the ImportBlocker initialization was
moved, because it has to happen inside the plugin specific interpreters.
Adapted the default sudo_printf from sudoers plugin to be able to print
errors before plugin open() gets called. (This is used by the multiple io
plugin loading to display error for too much plugin load.)
Since this makes us always have a sudo_log, I have removed the logic about
whether it is available or not.
When calling validate() python function, TypeError exception was thrown
("argument list must be a tuple"), because the call does not have
arguments, and python does not accept empty tuple for execution. NULL
must be used instead, which was handled as argument construction failure
previously.
IO/Group/Policy Python API version is displayed instead of sudo version,
because that is not very meaningful in this context.
They are only displayed in verbose mode.
Example plugins express it more concrete that they are displaying their
version, not the API version.