handle timeouts properly in the repeated (-r) ping mode (this fixes #457)
[helenos.git] / uspace / app / ping / ping.c
blob57caf3e915953628a88ad688eaefc82bd119d32c
1 /*
2 * Copyright (c) 2013 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup ping
30 * @{
32 /** @file ICMP echo utility.
35 #include <async.h>
36 #include <stdbool.h>
37 #include <errno.h>
38 #include <fibril_synch.h>
39 #include <net/socket_codes.h>
40 #include <inet/dnsr.h>
41 #include <inet/addr.h>
42 #include <inet/inetping.h>
43 #include <io/console.h>
44 #include <getopt.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <str.h>
48 #include <str_error.h>
49 #include <sys/types.h>
51 #define NAME "ping"
53 /** Delay between subsequent ping requests in microseconds */
54 #define PING_DELAY (1000 * 1000)
56 /** Ping request timeout in microseconds */
57 #define PING_TIMEOUT (1000 * 1000)
59 typedef enum {
60 RECEIVED_NONE,
61 RECEIVED_SUCCESS,
62 RECEIVED_INTERRUPT
63 } received_t;
65 static received_t received;
66 static FIBRIL_CONDVAR_INITIALIZE(received_cv);
67 static FIBRIL_MUTEX_INITIALIZE(received_lock);
69 static bool quit = false;
70 static FIBRIL_CONDVAR_INITIALIZE(quit_cv);
71 static FIBRIL_MUTEX_INITIALIZE(quit_lock);
73 static int ping_ev_recv(inetping_sdu_t *);
75 static inetping_ev_ops_t ev_ops = {
76 .recv = ping_ev_recv
79 static addr32_t src;
80 static addr32_t dest;
82 static bool repeat_forever = false;
83 static size_t repeat_count = 1;
85 static const char *short_options = "rn:";
87 static void print_syntax(void)
89 printf("Syntax: %s [-n <count>|-r] <host>\n", NAME);
92 static void ping_signal_received(received_t value)
94 fibril_mutex_lock(&received_lock);
95 received = value;
96 fibril_mutex_unlock(&received_lock);
97 fibril_condvar_broadcast(&received_cv);
100 static void ping_signal_quit(void)
102 fibril_mutex_lock(&quit_lock);
103 quit = true;
104 fibril_mutex_unlock(&quit_lock);
105 fibril_condvar_broadcast(&quit_cv);
108 static int ping_ev_recv(inetping_sdu_t *sdu)
110 inet_addr_t src_addr;
111 inet_addr_set(sdu->src, &src_addr);
113 inet_addr_t dest_addr;
114 inet_addr_set(sdu->dest, &dest_addr);
116 char *asrc;
117 int rc = inet_addr_format(&src_addr, &asrc);
118 if (rc != EOK)
119 return ENOMEM;
121 char *adest;
122 rc = inet_addr_format(&dest_addr, &adest);
123 if (rc != EOK) {
124 free(asrc);
125 return ENOMEM;
128 printf("Received ICMP echo reply: from %s to %s, seq. no %u, "
129 "payload size %zu\n", asrc, adest, sdu->seq_no, sdu->size);
131 ping_signal_received(RECEIVED_SUCCESS);
133 free(asrc);
134 free(adest);
135 return EOK;
138 static int ping_send(uint16_t seq_no)
140 inetping_sdu_t sdu;
142 sdu.src = src;
143 sdu.dest = dest;
144 sdu.seq_no = seq_no;
145 sdu.data = (void *) "foo";
146 sdu.size = 3;
148 int rc = inetping_send(&sdu);
149 if (rc != EOK)
150 printf("Failed sending echo request: %s (%d).\n",
151 str_error(rc), rc);
153 return rc;
156 static int transmit_fibril(void *arg)
158 uint16_t seq_no = 0;
160 while ((repeat_count--) || (repeat_forever)) {
161 fibril_mutex_lock(&received_lock);
162 received = RECEIVED_NONE;
163 fibril_mutex_unlock(&received_lock);
165 (void) ping_send(++seq_no);
167 fibril_mutex_lock(&received_lock);
168 int rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
169 PING_TIMEOUT);
170 received_t recv = received;
171 fibril_mutex_unlock(&received_lock);
173 if ((rc == ETIMEOUT) || (recv == RECEIVED_NONE))
174 printf("Echo request timed out (seq. no %u)\n", seq_no);
176 if (recv == RECEIVED_INTERRUPT)
177 break;
179 if ((repeat_count > 0) || (repeat_forever)) {
180 fibril_mutex_lock(&received_lock);
181 rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
182 PING_DELAY);
183 recv = received;
184 fibril_mutex_unlock(&received_lock);
186 if (recv == RECEIVED_INTERRUPT)
187 break;
191 ping_signal_quit();
192 return 0;
195 static int input_fibril(void *arg)
197 console_ctrl_t *con = console_init(stdin, stdout);
199 while (true) {
200 cons_event_t ev;
201 if (!console_get_event(con, &ev))
202 break;
204 if ((ev.type == CEV_KEY) && (ev.ev.key.type == KEY_PRESS) &&
205 ((ev.ev.key.mods & (KM_ALT | KM_SHIFT)) == 0) &&
206 ((ev.ev.key.mods & KM_CTRL) != 0)) {
207 /* Ctrl+key */
208 if (ev.ev.key.key == KC_Q) {
209 ping_signal_received(RECEIVED_INTERRUPT);
210 break;
215 return 0;
218 int main(int argc, char *argv[])
220 dnsr_hostinfo_t *hinfo = NULL;
221 char *asrc = NULL;
222 char *adest = NULL;
223 char *sdest = NULL;
225 int rc = inetping_init(&ev_ops);
226 if (rc != EOK) {
227 printf("Failed connecting to internet ping service: "
228 "%s (%d).\n", str_error(rc), rc);
229 goto error;
232 int c;
233 while ((c = getopt(argc, argv, short_options)) != -1) {
234 switch (c) {
235 case 'r':
236 repeat_forever = true;
237 break;
238 case 'n':
239 rc = str_size_t(optarg, NULL, 10, true, &repeat_count);
240 if (rc != EOK) {
241 printf("Invalid repeat count.\n");
242 print_syntax();
243 goto error;
245 break;
246 default:
247 printf("Unknown option passed.\n");
248 print_syntax();
249 goto error;
253 if (optind >= argc) {
254 printf("IP address or host name not supplied.\n");
255 print_syntax();
256 goto error;
259 /* Parse destination address */
260 inet_addr_t dest_addr;
261 rc = inet_addr_parse(argv[optind], &dest_addr);
262 if (rc != EOK) {
263 /* Try interpreting as a host name */
264 rc = dnsr_name2host(argv[optind], &hinfo, AF_INET);
265 if (rc != EOK) {
266 printf("Error resolving host '%s'.\n", argv[optind]);
267 goto error;
270 dest_addr = hinfo->addr;
273 uint16_t af = inet_addr_get(&dest_addr, &dest, NULL);
274 if (af != AF_INET) {
275 printf("Destination '%s' is not an IPv4 address.\n",
276 argv[optind]);
277 goto error;
280 /* Determine source address */
281 rc = inetping_get_srcaddr(dest, &src);
282 if (rc != EOK) {
283 printf("Failed determining source address.\n");
284 goto error;
287 inet_addr_t src_addr;
288 inet_addr_set(src, &src_addr);
290 rc = inet_addr_format(&src_addr, &asrc);
291 if (rc != EOK) {
292 printf("Out of memory.\n");
293 goto error;
296 rc = inet_addr_format(&dest_addr, &adest);
297 if (rc != EOK) {
298 printf("Out of memory.\n");
299 goto error;
302 if (hinfo != NULL) {
303 rc = asprintf(&sdest, "%s (%s)", hinfo->cname, adest);
304 if (rc < 0) {
305 printf("Out of memory.\n");
306 goto error;
308 } else {
309 sdest = adest;
310 adest = NULL;
313 printf("Sending ICMP echo request from %s to %s (Ctrl+Q to quit)\n",
314 asrc, sdest);
316 fid_t fid = fibril_create(transmit_fibril, NULL);
317 if (fid == 0) {
318 printf("Failed creating transmit fibril.\n");
319 goto error;
322 fibril_add_ready(fid);
324 fid = fibril_create(input_fibril, NULL);
325 if (fid == 0) {
326 printf("Failed creating input fibril.\n");
327 goto error;
330 fibril_add_ready(fid);
332 fibril_mutex_lock(&quit_lock);
333 while (!quit)
334 fibril_condvar_wait(&quit_cv, &quit_lock);
335 fibril_mutex_unlock(&quit_lock);
337 free(asrc);
338 free(adest);
339 free(sdest);
340 dnsr_hostinfo_destroy(hinfo);
341 return 0;
343 error:
344 free(asrc);
345 free(adest);
346 free(sdest);
347 dnsr_hostinfo_destroy(hinfo);
348 return 1;
351 /** @}