This makes possible to print possible parser error messages without requiring superuser credentials.
330 lines
9.9 KiB
Python
330 lines
9.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
|
|
@author: Fabio Erculiani <lxnay@sabayon.org>
|
|
@contact: lxnay@sabayon.org
|
|
@copyright: Fabio Erculiani
|
|
@license: GPL-2
|
|
|
|
B{Entropy Command Line Client}.
|
|
|
|
"""
|
|
import os
|
|
import sys
|
|
import errno
|
|
import tempfile
|
|
import argparse
|
|
import pdb
|
|
|
|
from entropy.i18n import _
|
|
from entropy.output import print_error, print_warning, bold, purple, \
|
|
teal, blue, darkred, darkgreen, readtext, print_generic, TextInterface
|
|
from entropy.const import etpConst, etpUi, const_convert_to_rawstring
|
|
from entropy.exceptions import SystemDatabaseError, OnlineMirrorError, \
|
|
RepositoryError, PermissionDenied, FileNotFound, SPMError
|
|
|
|
import entropy.tools
|
|
|
|
from solo.commands.descriptor import SoloCommandDescriptor
|
|
from solo.utils import read_client_release
|
|
|
|
def handle_exception(exc_class, exc_instance, exc_tb):
|
|
|
|
# restore original exception handler, to avoid loops
|
|
uninstall_exception_handler()
|
|
|
|
_text = TextInterface()
|
|
|
|
if exc_class is SystemDatabaseError:
|
|
_text.output(
|
|
darkred(_("Installed packages repository corrupted. "
|
|
"Please re-generate it")),
|
|
importance=1,
|
|
level="error")
|
|
raise SystemExit(101)
|
|
|
|
generic_exc_classes = (OnlineMirrorError, RepositoryError,
|
|
PermissionDenied, FileNotFound, SPMError, SystemError)
|
|
if exc_class in generic_exc_classes:
|
|
_text.output(
|
|
"%s: %s" % (exc_instance, darkred(_("Cannot continue")),),
|
|
importance=1,
|
|
level="error")
|
|
raise SystemExit(1)
|
|
|
|
if exc_class is SystemExit:
|
|
return
|
|
|
|
if exc_class is IOError:
|
|
if exc_instance.errno != errno.EPIPE:
|
|
return
|
|
|
|
if exc_class is KeyboardInterrupt:
|
|
raise SystemExit(1)
|
|
|
|
t_back = entropy.tools.get_traceback(tb_obj = exc_tb)
|
|
if etpUi['debug']:
|
|
sys.stdout = sys.__stdout__
|
|
sys.stderr = sys.__stderr__
|
|
sys.stdin = sys.__stdin__
|
|
entropy.tools.print_exception(tb_data = exc_tb)
|
|
pdb.set_trace()
|
|
|
|
if exc_class is OSError:
|
|
if exc_instance.errno == errno.ENOSPC:
|
|
print_generic(t_back)
|
|
_text.output(
|
|
"%s: %s" % (
|
|
exc_instance,
|
|
darkred(_("Your hard drive is full! Your fault!")),),
|
|
importance=1,
|
|
level="error")
|
|
raise SystemExit(5)
|
|
elif exc_instance.errno == errno.ENOMEM:
|
|
print_generic(t_back)
|
|
_text.output(
|
|
"%s: %s" % (
|
|
exc_instance,
|
|
darkred(_("No more memory dude! Your fault!")),),
|
|
importance=1,
|
|
level="error")
|
|
raise SystemExit(5)
|
|
|
|
_text.output(
|
|
darkred(_("Hi. My name is Bug Reporter. "
|
|
"I am sorry to inform you that the program crashed. "
|
|
"Well, you know, shit happens.")),
|
|
importance=1,
|
|
level="error")
|
|
_text.output(
|
|
darkred(_("But there's something you could "
|
|
"do to help me to be a better application.")),
|
|
importance=1,
|
|
level="error")
|
|
_text.output(
|
|
darkred(
|
|
_("-- BUT, DO NOT SUBMIT THE SAME REPORT MORE THAN ONCE --")),
|
|
importance=1,
|
|
level="error")
|
|
_text.output(
|
|
darkred(
|
|
_("Now I am showing you what happened. "
|
|
"Don't panic, I'm here to help you.")),
|
|
importance=1,
|
|
level="error")
|
|
|
|
entropy.tools.print_exception(tb_data = exc_tb)
|
|
|
|
exception_data = entropy.tools.print_exception(silent = True,
|
|
tb_data = exc_tb, all_frame_data = True)
|
|
exception_tback_raw = const_convert_to_rawstring(t_back)
|
|
|
|
error_fd, error_file = None, None
|
|
try:
|
|
error_fd, error_file = tempfile.mkstemp(
|
|
prefix="entropy.error.report.",
|
|
suffix=".txt")
|
|
|
|
with os.fdopen(error_fd, "wb") as ferror:
|
|
ferror.write(
|
|
const_convert_to_rawstring(
|
|
"\nRevision: %s\n\n" % (
|
|
etpConst['entropyversion'],))
|
|
)
|
|
ferror.write(
|
|
exception_tback_raw)
|
|
ferror.write(
|
|
const_convert_to_rawstring("\n\n"))
|
|
ferror.write(
|
|
const_convert_to_rawstring(''.join(exception_data)))
|
|
ferror.write(
|
|
const_convert_to_rawstring("\n"))
|
|
|
|
except (OSError, IOError) as err:
|
|
_text.output(
|
|
"%s: %s" % (
|
|
err,
|
|
darkred(
|
|
_("Oh well, I cannot even write to TMPDIR. "
|
|
"So, please copy the error and "
|
|
"mail lxnay@sabayon.org."))),
|
|
importance=1,
|
|
level="error")
|
|
raise SystemExit(1)
|
|
finally:
|
|
if error_fd is not None:
|
|
try:
|
|
os.close(error_fd)
|
|
except OSError:
|
|
pass
|
|
|
|
_text.output("", level="error")
|
|
|
|
ask_msg = _("Erm... Can I send the error, "
|
|
"along with some other information\nabout your "
|
|
"hardware to my creators so they can fix me? "
|
|
"(Your IP will be logged)")
|
|
rc = _text.ask_question(ask_msg)
|
|
if rc == _("No"):
|
|
_text.output(
|
|
darkgreen(_("Ok, ok ok ok... Sorry!")),
|
|
level="error")
|
|
raise SystemExit(2)
|
|
|
|
_text.output(
|
|
darkgreen(
|
|
_("If you want to be contacted back "
|
|
"(and actively supported), also answer "
|
|
"the questions below:")
|
|
),
|
|
level="error")
|
|
|
|
try:
|
|
name = readtext(_("Your Full name:"))
|
|
email = readtext(_("Your E-Mail address:"))
|
|
description = readtext(_("What you were doing:"))
|
|
except EOFError:
|
|
raise SystemExit(2)
|
|
|
|
try:
|
|
from entropy.client.interfaces.qa import UGCErrorReportInterface
|
|
from entropy.core.settings.base import SystemSettings
|
|
_settings = SystemSettings()
|
|
repository_id = _settings['repositories']['default_repository']
|
|
error = UGCErrorReportInterface(repository_id)
|
|
except (OnlineMirrorError, AttributeError, ImportError,):
|
|
error = None
|
|
|
|
result = None
|
|
if error is not None:
|
|
error.prepare(exception_tback_raw, name, email,
|
|
'\n'.join([x for x in exception_data]), description)
|
|
result = error.submit()
|
|
|
|
if result:
|
|
_text.output(
|
|
darkgreen(
|
|
_("Thank you very much. The error has been "
|
|
"reported and hopefully, the problem will "
|
|
"be solved as soon as possible.")),
|
|
level="error")
|
|
else:
|
|
_text.output(
|
|
darkred(_("Ugh. Cannot send the report. "
|
|
"Please mail the file below "
|
|
"to lxnay@sabayon.org.")),
|
|
level="error")
|
|
_text.output("", level="error")
|
|
_text.output("==> %s" % (error_file,), level="error")
|
|
_text.output("", level="error")
|
|
|
|
def install_exception_handler():
|
|
sys.excepthook = handle_exception
|
|
|
|
def uninstall_exception_handler():
|
|
sys.excepthook = sys.__excepthook__
|
|
|
|
def warn_version_mismatch():
|
|
equo_ver = read_client_release()
|
|
entropy_ver = etpConst['entropyversion']
|
|
if equo_ver != entropy_ver:
|
|
print_warning("")
|
|
print_warning("%s: %s" % (
|
|
bold(_("Entropy/Equo version mismatch")),
|
|
purple(_("it could make your system explode!")),))
|
|
print_warning("(%s [equo] & %s [entropy])" % (
|
|
blue(equo_ver),
|
|
blue(entropy_ver),))
|
|
print_warning("")
|
|
|
|
def warn_live_system():
|
|
print_warning("")
|
|
print_warning(
|
|
purple(_("Entropy is running off a Live System")))
|
|
print_warning(
|
|
teal(_("Performance and stability could get"
|
|
" severely compromised")))
|
|
print_warning("")
|
|
|
|
def main():
|
|
|
|
warn_version_mismatch()
|
|
|
|
install_exception_handler()
|
|
|
|
descriptors = SoloCommandDescriptor.obtain()
|
|
args_map = {}
|
|
catch_all = None
|
|
for descriptor in descriptors:
|
|
klass = descriptor.get_class()
|
|
if klass.CATCH_ALL:
|
|
catch_all = klass
|
|
args_map[klass.NAME] = klass
|
|
for alias in klass.ALIASES:
|
|
args_map[alias] = klass
|
|
|
|
args = sys.argv[1:]
|
|
is_bashcomp = False
|
|
if "--bashcomp" in args:
|
|
is_bashcomp = True
|
|
args.remove("--bashcomp")
|
|
# the first eit, because bash does:
|
|
# argv -> equo --bashcomp equo repo
|
|
# and we need to drop --bashcomp and
|
|
# argv[2]
|
|
args.pop(0)
|
|
|
|
cmd = None
|
|
last_arg = None
|
|
if args:
|
|
last_arg = args[-1]
|
|
cmd = args[0]
|
|
args = args[1:]
|
|
cmd_class = args_map.get(cmd)
|
|
yell_class = args_map.get("yell")
|
|
|
|
if cmd_class is None:
|
|
cmd_class = catch_all
|
|
|
|
cmd_obj = cmd_class(args)
|
|
if is_bashcomp:
|
|
try:
|
|
cmd_obj.bashcomp(last_arg)
|
|
except NotImplementedError:
|
|
pass
|
|
raise SystemExit(0)
|
|
|
|
# non-root users not allowed
|
|
allowed = True
|
|
if os.getuid() != 0 and \
|
|
cmd_class is not catch_all and \
|
|
not cmd_class.ALLOW_UNPRIVILEGED and \
|
|
"--help" not in args:
|
|
cmd_class = catch_all
|
|
allowed = False
|
|
|
|
if allowed:
|
|
|
|
if not cmd_class.ALLOW_UNPRIVILEGED:
|
|
if entropy.tools.islive():
|
|
warn_live_system()
|
|
|
|
func, func_args = cmd_obj.parse()
|
|
exit_st = func(*func_args)
|
|
if exit_st == -10:
|
|
# syntax error, yell at user
|
|
func, func_args = yell_class(args).parse()
|
|
func(*func_args)
|
|
raise SystemExit(10)
|
|
else:
|
|
yell_class.reset()
|
|
raise SystemExit(exit_st)
|
|
|
|
else:
|
|
# execute this anyway so that commands are
|
|
# incomplete or invalid, the command error
|
|
# message will take precedence.
|
|
_func, _func_args = cmd_obj.parse()
|
|
print_error(_("superuser access required"))
|
|
raise SystemExit(1)
|