source: mainline/uspace/app/ping/ping.c

Last change on this file was 87822ce, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Avoid infinite loop when console communication is broken

Need to make sure callers of console_get_event_timeout() can distinguish
between timeout and I/O error. Fix all callers of console_get_event()
and console_get_event_timeout() not to enter infinite loop when console
connection is broken. Also avoid setting of errno variable.

  • Property mode set to 100644
File size: 7.7 KB
Line 
1/*
2 * Copyright (c) 2013 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup ping
30 * @{
31 */
32/** @file ICMP echo utility.
33 */
34
35#include <async.h>
36#include <errno.h>
37#include <fibril_synch.h>
38#include <inet/addr.h>
39#include <inet/host.h>
40#include <inet/inetping.h>
41#include <io/console.h>
42#include <getopt.h>
43#include <stdbool.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <stddef.h>
47#include <stdint.h>
48#include <str.h>
49#include <str_error.h>
50
51#define NAME "ping"
52
53/** Delay between subsequent ping requests in microseconds */
54#define PING_DELAY (1000 * 1000)
55
56/** Ping request timeout in microseconds */
57#define PING_TIMEOUT (1000 * 1000)
58
59typedef enum {
60 RECEIVED_NONE,
61 RECEIVED_SUCCESS,
62 RECEIVED_INTERRUPT
63} received_t;
64
65static received_t received;
66static FIBRIL_CONDVAR_INITIALIZE(received_cv);
67static FIBRIL_MUTEX_INITIALIZE(received_lock);
68
69static bool quit = false;
70static FIBRIL_CONDVAR_INITIALIZE(quit_cv);
71static FIBRIL_MUTEX_INITIALIZE(quit_lock);
72
73static errno_t ping_ev_recv(inetping_sdu_t *);
74
75static inetping_ev_ops_t ev_ops = {
76 .recv = ping_ev_recv
77};
78
79static inet_addr_t src_addr;
80static inet_addr_t dest_addr;
81
82static bool repeat_forever = false;
83static size_t repeat_count = 1;
84
85static const char *short_options = "46rn:";
86
87static void print_syntax(void)
88{
89 printf("Syntax: %s [<options>] <host>\n", NAME);
90 printf("\t-n <count> Repeat the specified number of times\n");
91 printf("\t-r Repeat forever\n");
92 printf("\t-4|-6 Use IPv4 or IPv6 destination host address\n");
93}
94
95static void ping_signal_received(received_t value)
96{
97 fibril_mutex_lock(&received_lock);
98 received = value;
99 fibril_mutex_unlock(&received_lock);
100 fibril_condvar_broadcast(&received_cv);
101}
102
103static void ping_signal_quit(void)
104{
105 fibril_mutex_lock(&quit_lock);
106 quit = true;
107 fibril_mutex_unlock(&quit_lock);
108 fibril_condvar_broadcast(&quit_cv);
109}
110
111static errno_t ping_ev_recv(inetping_sdu_t *sdu)
112{
113 char *asrc;
114 errno_t rc = inet_addr_format(&src_addr, &asrc);
115 if (rc != EOK)
116 return ENOMEM;
117
118 char *adest;
119 rc = inet_addr_format(&dest_addr, &adest);
120 if (rc != EOK) {
121 free(asrc);
122 return ENOMEM;
123 }
124
125 printf("Received ICMP echo reply: from %s to %s, seq. no %u, "
126 "payload size %zu\n", asrc, adest, sdu->seq_no, sdu->size);
127
128 ping_signal_received(RECEIVED_SUCCESS);
129
130 free(asrc);
131 free(adest);
132 return EOK;
133}
134
135static errno_t ping_send(uint16_t seq_no)
136{
137 inetping_sdu_t sdu;
138
139 sdu.src = src_addr;
140 sdu.dest = dest_addr;
141 sdu.seq_no = seq_no;
142 sdu.data = (void *) "foo";
143 sdu.size = 3;
144
145 errno_t rc = inetping_send(&sdu);
146 if (rc != EOK)
147 printf("Failed sending echo request: %s: %s.\n",
148 str_error_name(rc), str_error(rc));
149
150 return rc;
151}
152
153static errno_t transmit_fibril(void *arg)
154{
155 uint16_t seq_no = 0;
156
157 while ((repeat_count--) || (repeat_forever)) {
158 fibril_mutex_lock(&received_lock);
159 received = RECEIVED_NONE;
160 fibril_mutex_unlock(&received_lock);
161
162 (void) ping_send(++seq_no);
163
164 fibril_mutex_lock(&received_lock);
165 errno_t rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
166 PING_TIMEOUT);
167 received_t recv = received;
168 fibril_mutex_unlock(&received_lock);
169
170 if ((rc == ETIMEOUT) || (recv == RECEIVED_NONE))
171 printf("Echo request timed out (seq. no %u)\n", seq_no);
172
173 if (recv == RECEIVED_INTERRUPT)
174 break;
175
176 if ((repeat_count > 0) || (repeat_forever)) {
177 fibril_mutex_lock(&received_lock);
178 rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
179 PING_DELAY);
180 recv = received;
181 fibril_mutex_unlock(&received_lock);
182
183 if (recv == RECEIVED_INTERRUPT)
184 break;
185 }
186 }
187
188 ping_signal_quit();
189 return 0;
190}
191
192static errno_t input_fibril(void *arg)
193{
194 console_ctrl_t *con = console_init(stdin, stdout);
195
196 while (true) {
197 cons_event_t ev;
198 errno_t rc;
199 rc = console_get_event(con, &ev);
200 if (rc != EOK) {
201 ping_signal_received(RECEIVED_INTERRUPT);
202 break;
203 }
204
205 if ((ev.type == CEV_KEY) && (ev.ev.key.type == KEY_PRESS) &&
206 ((ev.ev.key.mods & (KM_ALT | KM_SHIFT)) == 0) &&
207 ((ev.ev.key.mods & KM_CTRL) != 0)) {
208 /* Ctrl+key */
209 if (ev.ev.key.key == KC_Q) {
210 ping_signal_received(RECEIVED_INTERRUPT);
211 break;
212 }
213 }
214 }
215
216 return 0;
217}
218
219int main(int argc, char *argv[])
220{
221 char *asrc = NULL;
222 char *adest = NULL;
223 char *sdest = NULL;
224 char *host;
225 const char *errmsg;
226 ip_ver_t ip_ver = ip_any;
227
228 errno_t rc = inetping_init(&ev_ops);
229 if (rc != EOK) {
230 printf("Failed connecting to internet ping service: "
231 "%s: %s.\n", str_error_name(rc), str_error(rc));
232 goto error;
233 }
234
235 int c;
236 while ((c = getopt(argc, argv, short_options)) != -1) {
237 switch (c) {
238 case 'r':
239 repeat_forever = true;
240 break;
241 case 'n':
242 rc = str_size_t(optarg, NULL, 10, true, &repeat_count);
243 if (rc != EOK) {
244 printf("Invalid repeat count.\n");
245 print_syntax();
246 goto error;
247 }
248 break;
249 case '4':
250 ip_ver = ip_v4;
251 break;
252 case '6':
253 ip_ver = ip_v6;
254 break;
255 default:
256 printf("Unknown option passed.\n");
257 print_syntax();
258 goto error;
259 }
260 }
261
262 if (optind >= argc) {
263 printf("IP address or host name not supplied.\n");
264 print_syntax();
265 goto error;
266 }
267
268 host = argv[optind];
269
270 /* Look up host */
271 rc = inet_host_plookup_one(host, ip_ver, &dest_addr, NULL, &errmsg);
272 if (rc != EOK) {
273 printf("Error resolving host '%s' (%s).\n", host, errmsg);
274 goto error;
275 }
276
277 /* Determine source address */
278 rc = inetping_get_srcaddr(&dest_addr, &src_addr);
279 if (rc != EOK) {
280 printf("Failed determining source address.\n");
281 goto error;
282 }
283
284 rc = inet_addr_format(&src_addr, &asrc);
285 if (rc != EOK) {
286 printf("Out of memory.\n");
287 goto error;
288 }
289
290 rc = inet_addr_format(&dest_addr, &adest);
291 if (rc != EOK) {
292 printf("Out of memory.\n");
293 goto error;
294 }
295
296 if (asprintf(&sdest, "%s (%s)", host, adest) < 0) {
297 printf("Out of memory.\n");
298 goto error;
299 }
300
301 printf("Sending ICMP echo request from %s to %s (Ctrl+Q to quit)\n",
302 asrc, sdest);
303
304 fid_t fid = fibril_create(transmit_fibril, NULL);
305 if (fid == 0) {
306 printf("Failed creating transmit fibril.\n");
307 goto error;
308 }
309
310 fibril_add_ready(fid);
311
312 fid = fibril_create(input_fibril, NULL);
313 if (fid == 0) {
314 printf("Failed creating input fibril.\n");
315 goto error;
316 }
317
318 fibril_add_ready(fid);
319
320 fibril_mutex_lock(&quit_lock);
321 while (!quit)
322 fibril_condvar_wait(&quit_cv, &quit_lock);
323 fibril_mutex_unlock(&quit_lock);
324
325 free(asrc);
326 free(adest);
327 free(sdest);
328 return 0;
329
330error:
331 free(asrc);
332 free(adest);
333 free(sdest);
334 return 1;
335}
336
337/** @}
338 */
Note: See TracBrowser for help on using the repository browser.