usertools: fix handling EOF for telemetry input pipe
[dpdk.git] / usertools / dpdk-telemetry.py
1 #! /usr/bin/env python3
2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2020 Intel Corporation
4
5 """
6 Script to be used with V2 Telemetry.
7 Allows the user input commands and read the Telemetry response.
8 """
9
10 import socket
11 import os
12 import json
13 import errno
14 import readline
15 import argparse
16
17 # global vars
18 TELEMETRY_VERSION = "v2"
19 CMDS = []
20
21
22 def read_socket(sock, buf_len, echo=True):
23     """ Read data from socket and return it in JSON format """
24     reply = sock.recv(buf_len).decode()
25     try:
26         ret = json.loads(reply)
27     except json.JSONDecodeError:
28         print("Error in reply: ", reply)
29         sock.close()
30         raise
31     if echo:
32         print(json.dumps(ret))
33     return ret
34
35
36 def get_app_name(pid):
37     """ return the app name for a given PID, for printing """
38     proc_cmdline = os.path.join('/proc', str(pid), 'cmdline')
39     try:
40         with open(proc_cmdline) as f:
41             argv0 = f.read(1024).split('\0')[0]
42             return os.path.basename(argv0)
43     except IOError as e:
44         # ignore file not found errors
45         if e.errno != errno.ENOENT:
46             raise
47     return None
48
49
50 def handle_socket(path):
51     """ Connect to socket and handle user input """
52     sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
53     global CMDS
54     print("Connecting to " + path)
55     try:
56         sock.connect(path)
57     except OSError:
58         print("Error connecting to " + path)
59         sock.close()
60         return
61     json_reply = read_socket(sock, 1024)
62     output_buf_len = json_reply["max_output_len"]
63     app_name = get_app_name(json_reply["pid"])
64     if app_name:
65         print('Connected to application: "%s"' % app_name)
66
67     # get list of commands for readline completion
68     sock.send("/".encode())
69     CMDS = read_socket(sock, output_buf_len, False)["/"]
70
71     # interactive prompt
72     try:
73         text = input('--> ').strip()
74         while text != "quit":
75             if text.startswith('/'):
76                 sock.send(text.encode())
77                 read_socket(sock, output_buf_len)
78             text = input('--> ').strip()
79     except EOFError:
80         pass
81     finally:
82         sock.close()
83
84
85 def readline_complete(text, state):
86     """ Find any matching commands from the list based on user input """
87     all_cmds = ['quit'] + CMDS
88     if text:
89         matches = [c for c in all_cmds if c.startswith(text)]
90     else:
91         matches = all_cmds
92     return matches[state]
93
94
95 def get_dpdk_runtime_dir(fp):
96     """ Using the same logic as in DPDK's EAL, get the DPDK runtime directory
97     based on the file-prefix and user """
98     if (os.getuid() == 0):
99         return os.path.join('/var/run/dpdk', fp)
100     return os.path.join(os.environ.get('XDG_RUNTIME_DIR', '/tmp'), 'dpdk', fp)
101
102
103 readline.parse_and_bind('tab: complete')
104 readline.set_completer(readline_complete)
105 readline.set_completer_delims(readline.get_completer_delims().replace('/', ''))
106
107 parser = argparse.ArgumentParser()
108 parser.add_argument('-f', '--file-prefix', default='rte',
109                     help='Provide file-prefix for DPDK runtime directory')
110 args = parser.parse_args()
111 rd = get_dpdk_runtime_dir(args.file_prefix)
112 handle_socket(os.path.join(rd, 'dpdk_telemetry.{}'.format(TELEMETRY_VERSION)))