[apparmor] [PATCH 2/2] json support for tools (logprof and genprof)

Goldwyn Rodrigues rgoldwyn at suse.com
Wed Apr 12 11:27:12 UTC 2017


Hmm, I should have added a type field, but would we require a version
number as well?

On 04/11/2017 08:34 PM, Goldwyn Rodrigues wrote:
> From: Goldwyn Rodrigues <rgoldwyn at suse.com>
> 
> 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 <rgoldwyn at suse.com>
> ---
>  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 <kgupta8592 at gmail.com>
> +#    Copyright (C) 2017 Christian Boltz <apparmor at cboltz.de>
> +#    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()
>  
> 

-- 
Goldwyn



More information about the AppArmor mailing list