1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2020-2021 Huawei Device Co., Ltd.
5#
6# HDF is dual licensed: you can use it either under the terms of
7# the GPL, or the BSD license, at your option.
8# See the LICENSE file in the root of this repository for complete details.
9
10
11import sys
12import argparse
13
14from hdf_tool_exception import HdfToolException
15from .hdf_command_error_code import CommandErrorCode
16from .hdf_tool_commands import HdfToolCommands
17from .hdf_tool_argument_parser import HdfToolArgumentParser
18
19
20class Message(object):
21    TYPE_REQ = 'request'
22    TYPE_QUIT = 'quit'
23    TYPE_ERROR = 'error'
24    TYPE_SUCCESS = 'success'
25
26    def __init__(self, msg_type, content):
27        self.msg_type = msg_type
28        self.content = content
29
30    def is_quit(self):
31        return self.msg_type.lower() == Message.TYPE_ERROR
32
33
34def pack_message(msg_type, content, err_code=None):
35    body = bytearray(content, 'utf-8')
36    if not err_code:
37        header = bytearray('{},{}\n'.format(msg_type, len(body)), 'utf-8')
38    else:
39        header = bytearray('{},{},{}\n'.format(msg_type, len(body), err_code),
40                           'utf-8')
41    bytes_packet = bytearray().join([header, body])
42    return bytes_packet
43
44
45def decode_header(header):
46    header_parts = header.split(',')
47    if len(header_parts) < 2:
48        return -1, '', 0
49    msg_type = header_parts[0]
50    body_len = int(header_parts[1])
51    return 0, msg_type, body_len
52
53
54def decode_body(body):
55    arg_parser = HdfToolArgumentParser()
56    arg_parser.add_argument('cmd')
57    arg_parser.add_argument('remainder_args', nargs=argparse.REMAINDER)
58    args = arg_parser.parse_args(body.strip().split(' '))
59    remainder = [arg for arg in args.remainder_args if len(arg) != 0]
60    return args.cmd, remainder
61
62
63class HdfCommandLineServer(object):
64    def __init__(self, input_obj, output_obj):
65        self.input_obj = input_obj
66        self.output_obj = output_obj
67        self.commands = HdfToolCommands()
68
69    def _send_back(self, msg_type, content, error_code=None):
70        message_bytes = pack_message(msg_type, content, error_code)
71        self.output_obj.write(message_bytes)
72        self.output_obj.flush()
73
74    def _send_back_success(self, content):
75        self._send_back(Message.TYPE_SUCCESS, content)
76
77    def _send_back_error(self, error_code, content):
78        self._send_back(Message.TYPE_ERROR, content, error_code)
79
80    def _read_header(self):
81        head_bytes = self.input_obj.readline()
82        msg_header = str(head_bytes, encoding="utf-8")
83        return decode_header(msg_header)
84
85    def _read_body(self, body_len):
86        body_bytes = self.input_obj.read(body_len)
87        body = str(body_bytes, encoding='utf-8')
88        return decode_body(body)
89
90    def run(self):
91        while True:
92            try:
93                ret, msg_type, body_len = self._read_header()
94                if ret != 0:
95                    err_code = CommandErrorCode.MESSAGE_FORMAT_WRONG
96                    self._send_back_error(err_code, 'header wrong')
97                    continue
98                if msg_type == Message.TYPE_QUIT:
99                    self._send_back_success('Bye bye!')
100                    break
101                if body_len < 0:
102                    err_code = CommandErrorCode.MESSAGE_FORMAT_WRONG
103                    self._send_back_error(err_code, 'body len wrong')
104                    continue
105                cmd, args = self._read_body(body_len)
106                ret = self.commands.run(cmd, args)
107                if ret:
108                    self._send_back_success(str(ret))
109                else:
110                    self._send_back_success('')
111            except HdfToolException as exc:
112                try:
113                    self._send_back_error(exc.error_code, exc.exc_msg)
114                except OSError:
115                    sys.exit(-1)
116                finally:
117                    pass
118
119            except BaseException as exc:
120                try:
121                    self._send_back_error(CommandErrorCode.UNKNOWN_ERROR,
122                                          str(exc))
123                except OSError:
124                    sys.exit(-1)
125                finally:
126                    pass
127            finally:
128                pass
129