From: Goldwyn Rodrigues <[email protected]> This adds JSON support for tools in order to be able to talk to other utilities such as Yast.
The json is one per line, in order to differentiate between multiple records. This is based on work presented by Christian Boltz some time back. Signed-off-by: Goldwyn Rodrigues <[email protected]> --- utils/aa-genprof | 4 ++ utils/aa-logprof | 5 ++ utils/apparmor/ui.py | 192 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 124 insertions(+), 77 deletions(-) diff --git a/utils/aa-genprof b/utils/aa-genprof index e2e6544..9a8783d 100755 --- a/utils/aa-genprof +++ b/utils/aa-genprof @@ -61,8 +61,12 @@ parser = argparse.ArgumentParser(description=_('Generate profile for the given p parser.add_argument('-d', '--dir', type=str, help=_('path to profiles')) parser.add_argument('-f', '--file', type=str, help=_('path to logfile')) parser.add_argument('program', type=str, help=_('name of program to profile')) +parser.add_argument('-j', '--json', action="store_true", help=_('provide output in json format')) args = parser.parse_args() +if args.json: + aaui.set_json_mode() + profiling = args.program profiledir = args.dir diff --git a/utils/aa-logprof b/utils/aa-logprof index c05cbef..a00cfea 100755 --- a/utils/aa-logprof +++ b/utils/aa-logprof @@ -16,6 +16,7 @@ import argparse import os import apparmor.aa as apparmor +import apparmor.ui as aaui # setup exception handling from apparmor.fail import enable_aa_exception_handler @@ -29,8 +30,12 @@ parser = argparse.ArgumentParser(description=_('Process log entries to generate parser.add_argument('-d', '--dir', type=str, help=_('path to profiles')) parser.add_argument('-f', '--file', type=str, help=_('path to logfile')) parser.add_argument('-m', '--mark', type=str, help=_('mark in the log to start processing after')) +parser.add_argument('-j', '--json', action='store_true', help=_('provide the output in json format')) args = parser.parse_args() +if args.json: + aaui.set_json_mode() + profiledir = args.dir logmark = args.mark or '' diff --git a/utils/apparmor/ui.py b/utils/apparmor/ui.py index f25fff3..064cf74 100644 --- a/utils/apparmor/ui.py +++ b/utils/apparmor/ui.py @@ -1,5 +1,7 @@ # ---------------------------------------------------------------------- # Copyright (C) 2013 Kshitij Gupta <[email protected]> +# Copyright (C) 2017 Christian Boltz <[email protected]> +# Copyright (C) 2017 SUSE Linux # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -11,6 +13,8 @@ # GNU General Public License for more details. # # ---------------------------------------------------------------------- + +import json import sys import re import readline @@ -24,14 +28,20 @@ _ = init_translation() # Set up UI logger for separate messages from UI module debug_logger = DebugLogger('UI') -# The operating mode: yast or text, text by default -UI_mode = 'text' - # If Python3, wrap input in raw_input so make check passes if not 'raw_input' in dir(__builtins__): raw_input = input ARROWS = {'A': 'UP', 'B': 'DOWN', 'C': 'RIGHT', 'D': 'LEFT'} +UI_mode = 'text' + +def set_json_mode(): + global UI_mode + UI_mode = 'json' + +def write_json(jsonout): + sys.stdout.write(json.dumps(jsonout, sort_keys=False, separators=(',', ': ')) + '\n') + def getkey(): key = readkey() if key == '\x1B': @@ -44,12 +54,18 @@ def getkey(): def UI_Info(text): debug_logger.info(text) - if UI_mode == 'text': + if UI_mode == 'json': + jsonout = {'info': text} + write_json(jsonout) + else: # text mode sys.stdout.write(text + '\n') def UI_Important(text): debug_logger.debug(text) - if UI_mode == 'text': + if UI_mode == 'json': + jsonout = {'important': text} + write_json(jsonout) + else: # text mode: sys.stdout.write('\n' + text + '\n') def get_translated_hotkey(translated, cmsg=''): @@ -67,53 +83,57 @@ def get_translated_hotkey(translated, cmsg=''): def UI_YesNo(text, default): debug_logger.debug('UI_YesNo: %s: %s %s' % (UI_mode, text, default)) default = default.lower() - ans = None - if UI_mode == 'text': - yes = CMDS['CMD_YES'] - no = CMDS['CMD_NO'] - yeskey = get_translated_hotkey(yes).lower() - nokey = get_translated_hotkey(no).lower() - ans = 'XXXINVALIDXXX' - while ans not in ['y', 'n']: + yes = CMDS['CMD_YES'] + no = CMDS['CMD_NO'] + yeskey = get_translated_hotkey(yes).lower() + nokey = get_translated_hotkey(no).lower() + ans = 'XXXINVALIDXXX' + while ans not in ['y', 'n']: + if UI_mode == 'json': + jsonout = {'dialog': 'yesno', 'text': text, 'default': default} + write_json(jsonout) + else: # text mode: sys.stdout.write('\n' + text + '\n') if default == 'y': sys.stdout.write('\n[%s] / %s\n' % (yes, no)) else: sys.stdout.write('\n%s / [%s]\n' % (yes, no)) - ans = getkey() - if ans: - # Get back to english from localised answer - ans = ans.lower() - if ans == yeskey: - ans = 'y' - elif ans == nokey: - ans = 'n' - elif ans == 'left': - default = 'y' - elif ans == 'right': - default = 'n' - else: - ans = 'XXXINVALIDXXX' - continue # If user presses any other button ask again + ans = getkey() + if ans: + # Get back to english from localised answer + ans = ans.lower() + if ans == yeskey: + ans = 'y' + elif ans == nokey: + ans = 'n' + elif ans == 'left': + default = 'y' + elif ans == 'right': + default = 'n' else: - ans = default + ans = 'XXXINVALIDXXX' + continue # If user presses any other button ask again + else: + ans = default return ans def UI_YesNoCancel(text, default): debug_logger.debug('UI_YesNoCancel: %s: %s %s' % (UI_mode, text, default)) default = default.lower() - ans = None - if UI_mode == 'text': - yes = CMDS['CMD_YES'] - no = CMDS['CMD_NO'] - cancel = CMDS['CMD_CANCEL'] - - yeskey = get_translated_hotkey(yes).lower() - nokey = get_translated_hotkey(no).lower() - cancelkey = get_translated_hotkey(cancel).lower() - - ans = 'XXXINVALIDXXX' - while ans not in ['c', 'n', 'y']: + yes = CMDS['CMD_YES'] + no = CMDS['CMD_NO'] + cancel = CMDS['CMD_CANCEL'] + + yeskey = get_translated_hotkey(yes).lower() + nokey = get_translated_hotkey(no).lower() + cancelkey = get_translated_hotkey(cancel).lower() + + ans = 'XXXINVALIDXXX' + while ans not in ['c', 'n', 'y']: + if UI_mode == 'json': + jsonout = {'dialog': 'yesnocancel', 'text': text, 'default': default} + write_json(jsonout) + else: # text mode: sys.stdout.write('\n' + text + '\n') if default == 'y': sys.stdout.write('\n[%s] / %s / %s\n' % (yes, no, cancel)) @@ -121,55 +141,60 @@ def UI_YesNoCancel(text, default): sys.stdout.write('\n%s / [%s] / %s\n' % (yes, no, cancel)) else: sys.stdout.write('\n%s / %s / [%s]\n' % (yes, no, cancel)) - ans = getkey() - if ans: - # Get back to english from localised answer - ans = ans.lower() - if ans == yeskey: - ans = 'y' - elif ans == nokey: - ans = 'n' - elif ans == cancelkey: - ans = 'c' - elif ans == 'left': - if default == 'n': - default = 'y' - elif default == 'c': - default = 'n' - elif ans == 'right': - if default == 'y': - default = 'n' - elif default == 'n': - default = 'c' - else: - ans = default + ans = getkey() + if ans: + # Get back to english from localised answer + ans = ans.lower() + if ans == yeskey: + ans = 'y' + elif ans == nokey: + ans = 'n' + elif ans == cancelkey: + ans = 'c' + elif ans == 'left': + if default == 'n': + default = 'y' + elif default == 'c': + default = 'n' + elif ans == 'right': + if default == 'y': + default = 'n' + elif default == 'n': + default = 'c' + else: + ans = default return ans def UI_GetString(text, default): debug_logger.debug('UI_GetString: %s: %s %s' % (UI_mode, text, default)) string = default - if UI_mode == 'text': + if UI_mode == 'json': + jsonout = {'dialog': 'getstring', 'text': text, 'default': default} + write_json(jsonout) + else: # text mode: readline.set_startup_hook(lambda: readline.insert_text(default)) - try: - string = raw_input('\n' + text) - except EOFError: - string = '' - finally: - readline.set_startup_hook() + try: + string = raw_input('\n' + text) + except EOFError: + string = '' + finally: + readline.set_startup_hook() return string.strip() def UI_GetFile(file): debug_logger.debug('UI_GetFile: %s' % UI_mode) filename = None - if UI_mode == 'text': + if UI_mode == 'json': + jsonout = {'dialog': 'getfile', 'text': file['description']} + write_json(jsonout) + else: # text mode: sys.stdout.write(file['description'] + '\n') - filename = sys.stdin.read() + filename = sys.stdin.read() return filename def UI_BusyStart(message): debug_logger.debug('UI_BusyStart: %s' % UI_mode) - if UI_mode == 'text': - UI_Info(message) + UI_Info(message) def UI_BusyStop(): debug_logger.debug('UI_BusyStop: %s' % UI_mode) @@ -254,8 +279,7 @@ class PromptQuestion(object): def promptUser(self, params=''): cmd = None arg = None - if UI_mode == 'text': - cmd, arg = self.Text_PromptUser() + cmd, arg = self.Text_PromptUser() if cmd == 'CMD_ABORT': confirm_and_abort() cmd = 'XXXINVALIDXXX' @@ -324,6 +348,16 @@ class PromptQuestion(object): function_regexp += ')$' ans = 'XXXINVALIDXXX' + hdict = dict() + jsonprompt = { + 'title': title, + 'headers': hdict, + 'explanation': explanation, + 'options': options, + 'menu_items': menu_items, + 'default_key': default_key, + } + while not re.search(function_regexp, ans, flags=re.IGNORECASE): prompt = '\n' @@ -335,6 +369,7 @@ class PromptQuestion(object): while header_copy: header = header_copy.pop(0) value = header_copy.pop(0) + hdict[header] = value prompt += formatstr % (header + ':', value) prompt += '\n' @@ -352,7 +387,10 @@ class PromptQuestion(object): prompt += ' / '.join(menu_items) - sys.stdout.write(prompt + '\n') + if UI_mode == 'json': + write_json(jsonprompt) + else: # text mode: + sys.stdout.write(prompt + '\n') ans = getkey().lower() -- 2.10.2 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
