Path: blob/main/plugins/python/example_conversation.py
1532 views
import sudo1import signal2from os import path345class ReasonLoggerIOPlugin(sudo.Plugin):6"""7An example sudo plugin demonstrating how to use the sudo conversation API.89From the python plugin, you can ask something from the user using the10"sudo.conv" function. It expects one or more "sudo.ConvMessage" instances11which specifies how the interaction has to look like.1213sudo.ConvMessage has the following fields (see help(sudo.ConvMessage)):14msg_type: int Specifies the type of the conversation.15See sudo.CONV.* constants below.16timeout: int The maximum amount of time for the conversation17in seconds. After the timeout exceeds, the "sudo.conv"18function will raise sudo.ConversationInterrupted19exception.20msg: str The message to display for the user.2122To specify the conversion type you can use the following constants:23sudo.CONV.PROMPT_ECHO_OFF24sudo.CONV.PROMPT_ECHO_ON25sudo.CONV.ERROR_MSG26sudo.CONV.INFO_MSG27sudo.CONV.PROMPT_MASK28sudo.CONV.PROMPT_ECHO_OK29sudo.CONV.PREFER_TTY30"""3132def open(self, argv, command_info):33try:34conv_timeout = 120 # in seconds35sudo.log_info("Please provide your reason "36"for executing {}".format(argv))3738# We ask two questions, the second is not visible on screen,39# so the user can hide a hidden message in case of criminals are40# forcing him for running the command.41# You can either specify the arguments in strict order (timeout42# being optional), or use named arguments.43message1 = sudo.ConvMessage(sudo.CONV.PROMPT_ECHO_ON,44"Reason: ",45conv_timeout)46message2 = sudo.ConvMessage(msg="Secret reason: ",47timeout=conv_timeout,48msg_type=sudo.CONV.PROMPT_MASK)49reply1, reply2 = sudo.conv(message1, message2,50on_suspend=self.on_conversation_suspend,51on_resume=self.on_conversation_resume)5253with open(self._log_file_path(), "a") as file:54print("Executed", ' '.join(argv), file=file)55print("Reason:", reply1, file=file)56print("Hidden reason:", reply2, file=file)5758except sudo.ConversationInterrupted:59sudo.log_error("You did not answer in time")60return sudo.RC.REJECT6162def on_conversation_suspend(self, signum):63# This is just an example of how to do something on conversation64# suspend. You can skip specifying 'on_suspend' argument if there65# is no need66sudo.log_info("conversation suspend: signal",67self._signal_name(signum))6869def on_conversation_resume(self, signum):70# This is just an example of how to do something on conversation71# resume. You can skip specifying 'on_resume' argument if there72# is no need73sudo.log_info("conversation resume: signal was",74self._signal_name(signum))7576# helper functions:77if hasattr(signal, "Signals"):78@classmethod79def _signal_name(cls, signum: int):80try:81return signal.Signals(signum).name82except Exception:83return "{}".format(signum)84else:85@classmethod86def _signal_name(cls, signum: int):87for n, v in sorted(signal.__dict__.items()):88if v != signum:89continue90if n.startswith("SIG") and not n.startswith("SIG_"):91return n92return "{}".format(signum)9394def _log_file_path(self):95options_dict = sudo.options_as_dict(self.plugin_options)96log_path = options_dict.get("LogPath", "/tmp")97return path.join(log_path, "sudo_reasons.txt")9899100