nitdbg_client: exit if not connected (instead of getting a SIGPIPE)
[nit.git] / src / nitdbg_client.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Lucas Bajolet <lucas.bajolet@gmail.com>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Client for the nit debugger nitdbg-server
18 #
19 # Can send commands to the debugger
20 module nitdbg_client
21
22 import socket
23 import toolcontext
24
25 redef class ToolContext
26
27 var opt_host_address: OptionString = new OptionString("Sets the host to debug from, use IPV4 only (Defaults to 127.0.0.1)", "--host")
28 var opt_debug_port: OptionInt = new OptionInt("Sets the debug port (Defaults to 22125) - Must be contained between 0 and 65535", 22125, "--port")
29
30 redef init
31 do
32 super
33 self.option_context.add_option(self.opt_host_address)
34 self.option_context.add_option(self.opt_debug_port)
35 end
36
37 end
38
39 redef class String
40
41 # Checks if the actual string is a valid IPv4 address
42 # That is, if the pattern is int.int.int.int where each int must be between 0 and 255
43 fun is_valid_ipv4_address: Bool
44 do
45 var components = self.split_with(".")
46 if components.length != 4 then return false
47 for i in components do
48 if not i.is_numeric or not (i.to_i <= 255 and i.to_i >= 0) then return false
49 end
50 return true
51 end
52
53 end
54
55 # Persistant connection to the debugger
56 # Default port = 22125
57 #
58 class DebugClient
59
60 var debugger_connection: Socket
61
62 init (host: String, port: Int)
63 do
64 self.debugger_connection = new Socket.stream_with_host(host, port)
65 print "[HOST ADDRESS] : "+debugger_connection.address
66 print "[HOST] : "+debugger_connection.host.as(not null)
67 print "[PORT] : "+debugger_connection.port.to_s
68 end
69
70 init with_port (host: String, port: Int)
71 do
72 debugger_connection = new Socket.stream_with_host(host, port)
73 end
74
75 fun send_command(command: String)
76 do
77 debugger_connection.write(command+"\n")
78 end
79
80 fun connected: Bool
81 do
82 return self.debugger_connection.connected
83 end
84
85 fun ready: Bool
86 do
87 return debugger_connection.ready_to_read(40)
88 end
89
90 fun read_command: String
91 do
92 var buff = new Buffer
93 while debugger_connection.ready_to_read(40) do buff.append(debugger_connection.read)
94 return buff.to_s
95 end
96
97 fun disconnect
98 do
99 debugger_connection.close
100 end
101
102 end
103
104 # Create a tool context to handle options and paths
105 var toolcontext = new ToolContext
106 toolcontext.process_options
107
108 var debug: DebugClient
109
110 if toolcontext.opt_help.value then
111 toolcontext.option_context.usage
112 return
113 end
114
115 # If the port value is not an Int between 0 and 65535 (Mandatory according to the norm)
116 # Print the usage
117 if toolcontext.opt_debug_port.value < 0 or toolcontext.opt_debug_port.value > 65535 then
118 toolcontext.option_context.usage
119 return
120 end
121
122 # An IPV4 address does always complies to this form : x.x.x.x
123 # Where x is an integer whose value is >=0 and <= 255
124 if toolcontext.opt_host_address.value != null then
125 if toolcontext.opt_host_address.value.is_valid_ipv4_address then
126 debug = new DebugClient(toolcontext.opt_host_address.value.as(not null), toolcontext.opt_debug_port.value)
127 else
128 toolcontext.option_context.usage
129 return
130 end
131 else
132 debug = new DebugClient("127.0.0.1", toolcontext.opt_debug_port.value)
133 end
134
135 var res = debug.debugger_connection.connect
136 print "Connecting ... " + res.to_s
137 if not res then exit 1
138
139 var recv_cmd: String
140
141 var written_cmd: String
142
143 var over = false
144
145 while not over do
146 if stdin.poll_in then
147 written_cmd = gets
148 debug.send_command(written_cmd)
149 if written_cmd == "kill" then
150 over = true
151 end
152 end
153
154 if not over and debug.ready then
155 recv_cmd = debug.read_command
156 var command_parts = recv_cmd.split("\n")
157 for i in command_parts do
158 if i == "DBG DONE WORK ON SELF" then
159 debug.send_command("CLIENT DBG DONE ACK")
160 over = true
161 end
162 print i
163 end
164 end
165 end
166
167 debug.disconnect