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
RevLine 
[6428115]1/*
[c55cbbf]2 * Copyright (c) 2013 Jiri Svoboda
[6428115]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
[3b3c689]35#include <async.h>
[6428115]36#include <errno.h>
37#include <fibril_synch.h>
[3495654]38#include <inet/addr.h>
[a62ceaf]39#include <inet/host.h>
[6428115]40#include <inet/inetping.h>
[3b3c689]41#include <io/console.h>
[ccdc63e]42#include <getopt.h>
[8d2dd7f2]43#include <stdbool.h>
[6428115]44#include <stdio.h>
45#include <stdlib.h>
[8d2dd7f2]46#include <stddef.h>
47#include <stdint.h>
[ccdc63e]48#include <str.h>
49#include <str_error.h>
[6428115]50
51#define NAME "ping"
52
[3b3c689]53/** Delay between subsequent ping requests in microseconds */
54#define PING_DELAY (1000 * 1000)
55
[6428115]56/** Ping request timeout in microseconds */
57#define PING_TIMEOUT (1000 * 1000)
58
[ccdc63e]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);
[3b3c689]68
[ccdc63e]69static bool quit = false;
70static FIBRIL_CONDVAR_INITIALIZE(quit_cv);
71static FIBRIL_MUTEX_INITIALIZE(quit_lock);
72
[b7fd2a0]73static errno_t ping_ev_recv(inetping_sdu_t *);
[6428115]74
75static inetping_ev_ops_t ev_ops = {
76 .recv = ping_ev_recv
77};
78
[9749e47]79static inet_addr_t src_addr;
80static inet_addr_t dest_addr;
[3b3c689]81
[ccdc63e]82static bool repeat_forever = false;
83static size_t repeat_count = 1;
84
[9749e47]85static const char *short_options = "46rn:";
[3b3c689]86
[6428115]87static void print_syntax(void)
88{
[9749e47]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");
[6428115]93}
94
[ccdc63e]95static void ping_signal_received(received_t value)
[3b3c689]96{
[ccdc63e]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);
[3b3c689]109}
110
[b7fd2a0]111static errno_t ping_ev_recv(inetping_sdu_t *sdu)
[6428115]112{
[3e66428]113 char *asrc;
[b7fd2a0]114 errno_t rc = inet_addr_format(&src_addr, &asrc);
[6428115]115 if (rc != EOK)
116 return ENOMEM;
[a35b458]117
[3e66428]118 char *adest;
[a2e3ee6]119 rc = inet_addr_format(&dest_addr, &adest);
[6428115]120 if (rc != EOK) {
121 free(asrc);
122 return ENOMEM;
123 }
[a35b458]124
[6428115]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);
[a35b458]127
[ccdc63e]128 ping_signal_received(RECEIVED_SUCCESS);
[a35b458]129
[6428115]130 free(asrc);
131 free(adest);
132 return EOK;
133}
134
[b7fd2a0]135static errno_t ping_send(uint16_t seq_no)
[3b3c689]136{
137 inetping_sdu_t sdu;
[a35b458]138
[9749e47]139 sdu.src = src_addr;
140 sdu.dest = dest_addr;
[3b3c689]141 sdu.seq_no = seq_no;
142 sdu.data = (void *) "foo";
143 sdu.size = 3;
[a35b458]144
[b7fd2a0]145 errno_t rc = inetping_send(&sdu);
[ccdc63e]146 if (rc != EOK)
[c1694b6b]147 printf("Failed sending echo request: %s: %s.\n",
148 str_error_name(rc), str_error(rc));
[a35b458]149
[ccdc63e]150 return rc;
[3b3c689]151}
152
[b7fd2a0]153static errno_t transmit_fibril(void *arg)
[3b3c689]154{
155 uint16_t seq_no = 0;
[a35b458]156
[ccdc63e]157 while ((repeat_count--) || (repeat_forever)) {
158 fibril_mutex_lock(&received_lock);
159 received = RECEIVED_NONE;
160 fibril_mutex_unlock(&received_lock);
[a35b458]161
[3b3c689]162 (void) ping_send(++seq_no);
[a35b458]163
[ccdc63e]164 fibril_mutex_lock(&received_lock);
[b7fd2a0]165 errno_t rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
[ccdc63e]166 PING_TIMEOUT);
167 received_t recv = received;
168 fibril_mutex_unlock(&received_lock);
[a35b458]169
[ccdc63e]170 if ((rc == ETIMEOUT) || (recv == RECEIVED_NONE))
171 printf("Echo request timed out (seq. no %u)\n", seq_no);
[a35b458]172
[ccdc63e]173 if (recv == RECEIVED_INTERRUPT)
174 break;
[a35b458]175
[ccdc63e]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);
[a35b458]182
[ccdc63e]183 if (recv == RECEIVED_INTERRUPT)
184 break;
185 }
[3b3c689]186 }
[a35b458]187
[ccdc63e]188 ping_signal_quit();
[3b3c689]189 return 0;
190}
191
[b7fd2a0]192static errno_t input_fibril(void *arg)
[3b3c689]193{
[ccdc63e]194 console_ctrl_t *con = console_init(stdin, stdout);
[a35b458]195
[3b3c689]196 while (true) {
[ccdc63e]197 cons_event_t ev;
[87822ce]198 errno_t rc;
199 rc = console_get_event(con, &ev);
200 if (rc != EOK) {
201 ping_signal_received(RECEIVED_INTERRUPT);
[3b3c689]202 break;
[87822ce]203 }
[a35b458]204
[ccdc63e]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)) {
[3b3c689]208 /* Ctrl+key */
[07b7c48]209 if (ev.ev.key.key == KC_Q) {
[ccdc63e]210 ping_signal_received(RECEIVED_INTERRUPT);
211 break;
[3b3c689]212 }
213 }
214 }
[a35b458]215
[3b3c689]216 return 0;
217}
218
[6428115]219int main(int argc, char *argv[])
220{
[c55cbbf]221 char *asrc = NULL;
222 char *adest = NULL;
223 char *sdest = NULL;
[a62ceaf]224 char *host;
225 const char *errmsg;
[9749e47]226 ip_ver_t ip_ver = ip_any;
[a35b458]227
[b7fd2a0]228 errno_t rc = inetping_init(&ev_ops);
[6428115]229 if (rc != EOK) {
[ccdc63e]230 printf("Failed connecting to internet ping service: "
[c1694b6b]231 "%s: %s.\n", str_error_name(rc), str_error(rc));
[c55cbbf]232 goto error;
[6428115]233 }
[a35b458]234
[ccdc63e]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;
[9749e47]249 case '4':
250 ip_ver = ip_v4;
251 break;
252 case '6':
253 ip_ver = ip_v6;
254 break;
[ccdc63e]255 default:
256 printf("Unknown option passed.\n");
257 print_syntax();
258 goto error;
259 }
[3b3c689]260 }
[a35b458]261
[ccdc63e]262 if (optind >= argc) {
263 printf("IP address or host name not supplied.\n");
[6428115]264 print_syntax();
[c55cbbf]265 goto error;
[6428115]266 }
[a35b458]267
[a62ceaf]268 host = argv[optind];
[a35b458]269
[a62ceaf]270 /* Look up host */
271 rc = inet_host_plookup_one(host, ip_ver, &dest_addr, NULL, &errmsg);
[6428115]272 if (rc != EOK) {
[a62ceaf]273 printf("Error resolving host '%s' (%s).\n", host, errmsg);
274 goto error;
[6428115]275 }
[a35b458]276
[6428115]277 /* Determine source address */
[9749e47]278 rc = inetping_get_srcaddr(&dest_addr, &src_addr);
[6428115]279 if (rc != EOK) {
[ccdc63e]280 printf("Failed determining source address.\n");
[c55cbbf]281 goto error;
282 }
[a35b458]283
[a2e3ee6]284 rc = inet_addr_format(&src_addr, &asrc);
[c55cbbf]285 if (rc != EOK) {
[ccdc63e]286 printf("Out of memory.\n");
[c55cbbf]287 goto error;
288 }
[a35b458]289
[a2e3ee6]290 rc = inet_addr_format(&dest_addr, &adest);
[c55cbbf]291 if (rc != EOK) {
[ccdc63e]292 printf("Out of memory.\n");
[c55cbbf]293 goto error;
294 }
[a35b458]295
[d5c1051]296 if (asprintf(&sdest, "%s (%s)", host, adest) < 0) {
[a62ceaf]297 printf("Out of memory.\n");
298 goto error;
[6428115]299 }
[a35b458]300
[ccdc63e]301 printf("Sending ICMP echo request from %s to %s (Ctrl+Q to quit)\n",
[c55cbbf]302 asrc, sdest);
[a35b458]303
[ccdc63e]304 fid_t fid = fibril_create(transmit_fibril, NULL);
305 if (fid == 0) {
306 printf("Failed creating transmit fibril.\n");
307 goto error;
[3b3c689]308 }
[a35b458]309
[ccdc63e]310 fibril_add_ready(fid);
[a35b458]311
[ccdc63e]312 fid = fibril_create(input_fibril, NULL);
313 if (fid == 0) {
314 printf("Failed creating input fibril.\n");
[c55cbbf]315 goto error;
[6428115]316 }
[a35b458]317
[ccdc63e]318 fibril_add_ready(fid);
[a35b458]319
[ccdc63e]320 fibril_mutex_lock(&quit_lock);
321 while (!quit)
322 fibril_condvar_wait(&quit_cv, &quit_lock);
323 fibril_mutex_unlock(&quit_lock);
[a35b458]324
[c55cbbf]325 free(asrc);
326 free(adest);
327 free(sdest);
[6428115]328 return 0;
[a35b458]329
[c55cbbf]330error:
331 free(asrc);
332 free(adest);
333 free(sdest);
334 return 1;
[6428115]335}
336
337/** @}
338 */
Note: See TracBrowser for help on using the repository browser.