summaryrefslogtreecommitdiff
path: root/python/vyos/debug.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos/debug.py')
-rw-r--r--python/vyos/debug.py205
1 files changed, 205 insertions, 0 deletions
diff --git a/python/vyos/debug.py b/python/vyos/debug.py
new file mode 100644
index 000000000..6ce42b173
--- /dev/null
+++ b/python/vyos/debug.py
@@ -0,0 +1,205 @@
+# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+from datetime import datetime
+
+def message(message, flag='', destination=sys.stdout):
+ """
+ print a debug message line on stdout if debugging is enabled for the flag
+ also log it to a file if the flag 'log' is enabled
+
+ message: the message to print
+ flag: which flag must be set for it to print
+ destination: which file like object to write to (default: sys.stdout)
+
+ returns if any message was logged or not
+ """
+ enable = enabled(flag)
+ if enable:
+ destination.write(_format(flag,message))
+
+ # the log flag is special as it logs all the commands
+ # executed to a log
+ logfile = _logfile('log', '/tmp/developer-log')
+ if not logfile:
+ return enable
+
+ try:
+ # at boot the file is created as root:vyattacfg
+ # at runtime the file is created as user:vyattacfg
+ # but the helper scripts are not run as this so it
+ # need the default permission to be 666 (an not 660)
+ mask = os.umask(0o111)
+
+ with open(logfile, 'a') as f:
+ f.write(_timed(_format('log', message)))
+ finally:
+ os.umask(mask)
+
+ return enable
+
+
+def enabled(flag):
+ """
+ a flag can be set by touching the file in /tmp or /config
+
+ The current flags are:
+ - developer: the code will drop into PBD on un-handled exception
+ - log: the code will log all command to a file
+ - ifconfig: when modifying an interface,
+ prints command with result and sysfs access on stdout for interface
+ - command: print command run with result
+
+ Having the flag setup on the filesystem is required to have
+ debuging at boot time, however, setting the flag via environment
+ does not require a seek to the filesystem and is more efficient
+ it can be done on the shell on via .bashrc for the user
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns either the file or environment name used to set it up
+ """
+
+ # this is to force all new flags to be registered here to be
+ # documented both here and a reminder to update readthedocs :-)
+ if flag not in ['developer', 'log', 'ifconfig', 'command']:
+ return ''
+
+ return _fromenv(flag) or _fromfile(flag)
+
+
+def _timed(message):
+ now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ return f'{now} {message}'
+
+
+def _remove_invisible(string):
+ for char in ('\0', '\a', '\b', '\f', '\v'):
+ string = string.replace(char, '')
+ return string
+
+
+def _format(flag, message):
+ """
+ format a log message
+ """
+ message = _remove_invisible(message)
+ return f'DEBUG/{flag.upper():<7} {message}\n'
+
+
+def _fromenv(flag):
+ """
+ check if debugging is set for this flag via environment
+
+ For a given debug flag named "test"
+ The presence of the environment VYOS_TEST_DEBUG (uppercase) enables it
+
+ return empty string if not
+ return content of env value it is
+ """
+
+ flagname = f'VYOS_{flag.upper()}_DEBUG'
+ flagenv = os.environ.get(flagname, None)
+
+ if flagenv is None:
+ return ''
+ return flagenv
+
+
+def _fromfile(flag):
+ """
+ Check if debug exist for a given debug flag name
+
+ Check is a debug flag was set by the user. the flag can be set either:
+ - in /tmp for a non-persistent presence between reboot
+ - in /config for always on (an existence at boot time)
+
+ For a given debug flag named "test"
+ The presence of the file vyos.test.debug (all lowercase) enables it
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns the full flagname
+ """
+
+ for folder in ('/tmp', '/config'):
+ flagfile = f'{folder}/vyos.{flag}.debug'
+ if os.path.isfile(flagfile):
+ return flagfile
+
+ return ''
+
+
+def _contentenv(flag):
+ return os.environ.get(f'VYOS_{flag.upper()}_DEBUG', '').strip()
+
+
+def _contentfile(flag, default=''):
+ """
+ Check if debug exist for a given debug flag name
+
+ Check is a debug flag was set by the user. the flag can be set either:
+ - in /tmp for a non-persistent presence between reboot
+ - in /config for always on (an existence at boot time)
+
+ For a given debug flag named "test"
+ The presence of the file vyos.test.debug (all lowercase) enables it
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns the full flagname
+ """
+
+ for folder in ('/tmp', '/config'):
+ flagfile = f'{folder}/vyos.{flag}.debug'
+ if not os.path.isfile(flagfile):
+ continue
+ with open(flagfile) as f:
+ content = f.readline().strip()
+ return content or default
+
+ return ''
+
+
+def _logfile(flag, default):
+ """
+ return the name of the file to use for logging when the flag 'log' is set
+ if it could not be established or the location is invalid it returns
+ an empty string
+ """
+
+ # For log we return the location of the log file
+ log_location = _contentenv(flag) or _contentfile(flag, default)
+
+ # it was not set
+ if not log_location:
+ return ''
+
+ # Make sure that the logs can only be in /tmp, /var/log, or /tmp
+ if not log_location.startswith('/tmp/') and \
+ not log_location.startswith('/config/') and \
+ not log_location.startswith('/var/log/'):
+ return default
+ # Do not allow to escape the folders
+ if '..' in log_location:
+ return default
+
+ if not os.path.exists(log_location):
+ return log_location
+
+ # this permission is unique the the config and var folder
+ stat = os.stat(log_location).st_mode
+ if stat != 0o100666:
+ return default
+ return log_location