1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005-2006 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@ingroup su_root_ex
26 * @CFILE su_source_test.c
27 *
28 * @brief Test program for glib and su root event loop integration.
29 *
30 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
31 *
32 * @date Created: Thu Mar 18 19:40:51 1999 pessi
33 */
34
35 #include "config.h"
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <signal.h>
41
42 #include <assert.h>
43
44 struct pinger;
45 #define SU_ROOT_MAGIC_T struct pinger
46 #define SU_INTERNAL_P su_root_t *
47 #define SU_MSG_ARG_T su_sockaddr_t
48
49 #include "sofia-sip/su.h"
50 #include "sofia-sip/su_wait.h"
51 #include "sofia-sip/su_log.h"
52
53 #include <glib/gthread.h>
54 #include "sofia-sip/su_glib.h"
55
56 struct pinger {
57 enum { PINGER = 1, PONGER = 2 } const sort;
58 char const * name;
59 unsigned running : 1;
60 unsigned : 0;
61 su_root_t *root;
62 su_socket_t s;
63 su_timer_t *t;
64 int id;
65 int rindex;
66 su_time_t when;
67 su_sockaddr_t addr;
68 double rtt_total;
69 int rtt_n;
70 };
71
72 short opt_family = AF_INET;
73 short opt_verbatim = 0;
74 short opt_singlethread = 0;
75 GMainLoop *global_gmainloop = NULL;
76
udpsocket(void)77 static su_socket_t udpsocket(void)
78 {
79 su_socket_t s;
80 su_sockaddr_t su = { 0 };
81 socklen_t sulen = sizeof(su);
82 char nbuf[64];
83
84 su.su_family = opt_family;
85
86 su_getlocalip(&su);
87
88 s = su_socket(su.su_family, SOCK_DGRAM, 0);
89 if (s == INVALID_SOCKET) {
90 su_perror("udpsocket: socket");
91 exit(1);
92 }
93
94 if (bind(s, &su.su_sa, su_sockaddr_size(&su)) == SOCKET_ERROR) {
95 su_perror("udpsocket: bind");
96 exit(1);
97 }
98
99 if (getsockname(s, &su.su_sa, &sulen) == SOCKET_ERROR) {
100 su_perror("udpsocket: getsockname");
101 exit(1);
102 }
103
104 if (opt_verbatim)
105 printf("udpsocket: using address [%s]:%u\n",
106 inet_ntop(su.su_family, SU_ADDR(&su), nbuf, sizeof(nbuf)),
107 ntohs(su.su_sin.sin_port));
108
109 return s;
110 }
111
snow(su_time_t now)112 static char *snow(su_time_t now)
113 {
114 static char buf[24];
115
116 su_time_print(buf, sizeof(buf), &now);
117
118 return buf;
119 }
120
121 void
do_ping(struct pinger * p,su_timer_t * t,void * p0)122 do_ping(struct pinger *p, su_timer_t *t, void *p0)
123 {
124 char buf[1024];
125
126 assert(p == su_root_magic(su_timer_root(t)));
127 assert(p->sort == PINGER);
128
129 p->when = su_now();
130
131 snprintf(buf, sizeof(buf), "Ping %d at %s", p->id++, snow(p->when));
132 if (sendto(p->s, buf, strlen(buf), 0,
133 &p->addr.su_sa, su_sockaddr_size(&p->addr)) == -1) {
134 su_perror("do_ping: send");
135 }
136
137 if (opt_verbatim) {
138 puts(buf);
139 fflush(stdout);
140 }
141 }
142
143 int
do_rtt(struct pinger * p,su_wait_t * w,void * p0)144 do_rtt(struct pinger *p, su_wait_t *w, void *p0)
145 {
146 su_sockaddr_t su;
147 struct sockaddr * const susa = &su.su_sa;
148 socklen_t susize[] = { sizeof(su)};
149 char buf[1024];
150 char nbuf[1024];
151 int n;
152 su_time_t now = su_now();
153 double rtt;
154
155 assert(p0 == p);
156 assert(p->sort == PINGER);
157
158 rtt = su_time_diff(now, p->when);
159
160 p->rtt_total += rtt, p->rtt_n++;
161
162 su_wait_events(w, p->s);
163
164 n = recvfrom(p->s, buf, sizeof(buf) - 1, 0, susa, susize);
165 if (n < 0) {
166 su_perror("do_rtt: recvfrom");
167 return 0;
168 }
169 buf[n] = 0;
170
171 if (opt_verbatim)
172 printf("do_rtt: %d bytes from [%s]:%u: \"%s\", rtt = %lg ms\n",
173 n, inet_ntop(su.su_family, SU_ADDR(&su), nbuf, sizeof(nbuf)),
174 ntohs(su.su_sin.sin_port), buf, rtt / 1000);
175
176 do_ping(p, p->t, NULL);
177
178 return 0;
179 }
180
181 void
do_pong(struct pinger * p,su_timer_t * t,void * p0)182 do_pong(struct pinger *p, su_timer_t *t, void *p0)
183 {
184 char buf[1024];
185
186 assert(p == su_root_magic(su_timer_root(t)));
187 assert(p->sort == PONGER);
188
189 p->id = 0;
190
191 snprintf(buf, sizeof(buf), "Pong at %s", snow(su_now()));
192 if (sendto(p->s, buf, strlen(buf), 0,
193 &p->addr.su_sa, su_sockaddr_size(&p->addr)) == -1) {
194 su_perror("do_pong: send");
195 }
196
197 if (opt_verbatim) {
198 puts(buf);
199 fflush(stdout);
200 }
201 }
202
203 int
do_recv(struct pinger * p,su_wait_t * w,void * p0)204 do_recv(struct pinger *p, su_wait_t *w, void *p0)
205 {
206 su_sockaddr_t su;
207 socklen_t susize[] = { sizeof(su)};
208 char buf[1024];
209 char nbuf[1024];
210 int n;
211 su_time_t now = su_now();
212
213 assert(p0 == p);
214 assert(p->sort == PONGER);
215
216 su_wait_events(w, p->s);
217
218 n = recvfrom(p->s, buf, sizeof(buf) - 1, 0, &su.su_sa, susize);
219 if (n < 0) {
220 su_perror("do_recv: recvfrom");
221 return 0;
222 }
223 buf[n] = 0;
224
225 if (opt_verbatim)
226 printf("do_recv: %d bytes from [%s]:%u: \"%s\" at %s\n",
227 n, inet_ntop(su.su_family, SU_ADDR(&su), nbuf, sizeof(nbuf)),
228 ntohs(su.su_sin.sin_port), buf, snow(now));
229
230 fflush(stdout);
231
232 #if 0
233 if (p->id)
234 puts("do_recv: already a pending reply");
235
236 if (su_timer_set(p->t, do_pong, p) < 0) {
237 fprintf(stderr, "do_recv: su_timer_set() error\n");
238 return 0;
239 }
240
241 p->id = 1;
242 #else
243 do_pong(p, p->t, NULL);
244 #endif
245
246 return 0;
247 }
248
249 void
do_exit(struct pinger * x,su_timer_t * t,void * x0)250 do_exit(struct pinger *x, su_timer_t *t, void *x0)
251 {
252 g_assert(global_gmainloop);
253 if (opt_verbatim)
254 printf("do_exit at %s\n", snow(su_now()));
255 g_main_loop_quit(global_gmainloop);
256 }
257
258 int
do_init(su_root_t * root,struct pinger * p)259 do_init(su_root_t *root, struct pinger *p)
260 {
261 su_wait_t w;
262 su_socket_t s;
263 long interval;
264 su_timer_t *t;
265 su_wakeup_f f;
266 int index, index0;
267
268 switch (p->sort) {
269 case PINGER: f = do_rtt; interval = 200; break;
270 case PONGER: f = do_recv; interval = 40; break;
271 default:
272 return SU_FAILURE;
273 }
274
275 /* Create a sockets, */
276 s = udpsocket();
277 if (su_wait_create(&w, s, SU_WAIT_IN) == SOCKET_ERROR)
278 su_perror("su_wait_create"), exit(1);
279
280 p->s = s;
281 p->t = t = su_timer_create(su_root_task(root), interval);
282 if (t == NULL) {
283 su_perror("su_timer_create");
284 return SU_FAILURE;
285 }
286
287 index0 = su_root_register(root, &w, f, p, 0);
288 if (index0 == SOCKET_ERROR) {
289 su_perror("su_root_register");
290 return SU_FAILURE;
291 }
292
293 index = su_root_register(root, &w, f, p, 0);
294 if (index == SOCKET_ERROR) {
295 su_perror("su_root_register");
296 return SU_FAILURE;
297 }
298
299 su_root_deregister(root, index0);
300
301 p->rindex = index;
302
303 return 0;
304 }
305
306 void
do_destroy(su_root_t * root,struct pinger * p)307 do_destroy(su_root_t *root, struct pinger *p)
308 {
309 if (opt_verbatim)
310 printf("do_destroy %s at %s\n", p->name, snow(su_now()));
311 su_root_deregister(root, p->rindex);
312 su_timer_destroy(p->t), p->t = NULL;
313 p->running = 0;
314 }
315
316 void
start_ping(struct pinger * p,su_msg_r msg,su_sockaddr_t * arg)317 start_ping(struct pinger *p, su_msg_r msg, su_sockaddr_t *arg)
318 {
319 if (!p->running)
320 return;
321
322 if (opt_verbatim)
323 printf("start_ping: %s\n", p->name);
324
325 p->addr = *arg;
326 p->id = 1;
327 su_timer_set_at(p->t, do_ping, p, su_now());
328 }
329
330 void
start_pong(struct pinger * p,su_msg_r msg,su_sockaddr_t * arg)331 start_pong(struct pinger *p, su_msg_r msg, su_sockaddr_t *arg)
332 {
333 su_msg_r reply;
334
335 if (!p->running)
336 return;
337
338 if (opt_verbatim)
339 printf("start_pong: %s\n", p->name);
340
341 p->addr = *arg;
342
343 if (su_msg_reply(reply, msg, start_ping, sizeof(p->addr)) == 0) {
344 socklen_t sinsize[1] = { sizeof(p->addr) };
345 if (getsockname(p->s, (struct sockaddr*)su_msg_data(reply), sinsize)
346 == SOCKET_ERROR)
347 su_perror("start_pong: getsockname()"), exit(1);
348 su_msg_send(reply);
349 }
350 else {
351 fprintf(stderr, "su_msg_create failed!\n");
352 }
353 }
354
355 void
init_ping(struct pinger * p,su_msg_r msg,su_sockaddr_t * arg)356 init_ping(struct pinger *p, su_msg_r msg, su_sockaddr_t *arg)
357 {
358 su_msg_r reply;
359
360 if (opt_verbatim)
361 printf("init_ping: %s\n", p->name);
362
363 if (su_msg_reply(reply, msg, start_pong, sizeof(p->addr)) == 0) {
364 socklen_t sinsize[1] = { sizeof(p->addr) };
365 if (getsockname(p->s, (struct sockaddr*)su_msg_data(reply), sinsize)
366 == SOCKET_ERROR)
367 su_perror("start_pong: getsockname()"), exit(1);
368 su_msg_send(reply);
369 }
370 else {
371 fprintf(stderr, "su_msg_reply failed!\n");
372 }
373 }
374
375 #if HAVE_SIGNAL
376 static
term(int n)377 RETSIGTYPE term(int n)
378 {
379 exit(1);
380 }
381 #endif
382
383 void
time_test(void)384 time_test(void)
385 {
386 su_time_t now = su_now(), then = now;
387 su_duration_t t1, t2;
388 su_duration_t us;
389
390 for (us = 0; us < 1000000; us += 300) {
391 then.tv_sec = now.tv_sec;
392 if ((then.tv_usec = now.tv_usec + us) >= 1000000)
393 then.tv_usec -= 1000000, then.tv_sec++;
394 t1 = su_duration(now, then);
395 t2 = su_duration(then, now);
396 assert(t1 == -t2);
397 }
398
399 if (opt_verbatim)
400 printf("time_test: passed\n");
401 }
402
403 char const name[] = "su_test";
404
405 void
usage(int exitcode)406 usage(int exitcode)
407 {
408 fprintf(stderr, "usage: %s [-6vs] [pid]\n", name);
409 exit(exitcode);
410 }
411
412 /*
413 * test su_wait functionality:
414 *
415 * Create a ponger, waking up do_recv() when data arrives,
416 * then scheduling do_pong() by timer
417 *
418 * Create a pinger, executed from timer, scheduling do_ping(),
419 * waking up do_rtt() when data arrives
420 *
421 * Create a timer, executing do_exit() after 10 seconds
422 */
main(int argc,char * argv[])423 int main(int argc, char *argv[])
424 {
425 su_root_t *root;
426 su_clone_r ping = SU_CLONE_R_INIT, pong = SU_CLONE_R_INIT;
427 su_msg_r start_msg = SU_MSG_R_INIT;
428 su_timer_t *t;
429 unsigned long sleeppid = 0;
430
431 struct pinger
432 pinger = { PINGER, "ping", 1 },
433 ponger = { PONGER, "pong", 1 };
434
435 char *argv0 = argv[0];
436
437 #if HAVE_OPEN_C
438 dup2(1, 2);
439 #endif
440
441 while (argv[1]) {
442 if (strcmp(argv[1], "-v") == 0) {
443 opt_verbatim = 1;
444 argv++;
445 }
446 #if SU_HAVE_IN6
447 else if (strcmp(argv[1], "-6") == 0) {
448 opt_family = AF_INET6;
449 argv++;
450 }
451 #endif
452 else if (strcmp(argv[1], "-s") == 0) {
453 opt_singlethread = 1;
454 argv++;
455 }
456 else if (strlen(argv[1]) == strspn(argv[1], "0123456789")) {
457 sleeppid = strtoul(argv[1], NULL, 10);
458 argv++;
459 }
460 else {
461 usage(1);
462 }
463 }
464
465 #if HAVE_OPEN_C
466 opt_verbatim = 1;
467 opt_singlethread = 1;
468 su_log_soft_set_level(su_log_default, 9);
469 #endif
470
471 #if HAVE_SIGNAL
472 signal(SIGTERM, term);
473 #endif
474
475 su_init(); atexit(su_deinit);
476
477 time_test();
478
479 global_gmainloop = g_main_loop_new(NULL, FALSE);
480 g_assert(global_gmainloop);
481
482 root = su_glib_root_create(NULL);
483
484 if (!root) perror("su_root_glib_create"), exit(1);
485
486 if (!g_source_attach(su_glib_root_gsource(root), g_main_loop_get_context(global_gmainloop)))
487 perror("g_source_attach"), exit(1);
488
489 su_root_threading(root, 0 && !opt_singlethread);
490
491 if (su_clone_start(root, ping, &pinger, do_init, do_destroy) != 0)
492 perror("su_clone_start"), exit(1);
493 if (su_clone_start(root, pong, &ponger, do_init, do_destroy) != 0)
494 perror("su_clone_start"), exit(1);
495
496 /* Test timer, exiting after 200 milliseconds */
497 t = su_timer_create(su_root_task(root), 200L);
498 if (t == NULL)
499 su_perror("su_timer_create"), exit(1);
500 su_timer_set(t, (su_timer_f)do_exit, NULL);
501
502 su_msg_create(start_msg, su_clone_task(ping), su_clone_task(pong),
503 init_ping, 0);
504 su_msg_send(start_msg);
505
506 g_main_loop_run(global_gmainloop);
507
508 su_clone_wait(root, ping);
509 su_clone_wait(root, pong);
510
511 su_timer_destroy(t);
512
513 if (pinger.rtt_n) {
514 printf("%s executed %u pings in %g, mean rtt=%g sec\n", name,
515 pinger.rtt_n, pinger.rtt_total, pinger.rtt_total / pinger.rtt_n);
516 }
517 su_root_destroy(root);
518
519 g_main_loop_unref(global_gmainloop), global_gmainloop = NULL;
520
521 if (opt_verbatim)
522 printf("%s exiting\n", argv0);
523
524 #ifndef HAVE_WIN32
525 #if HAVE_SIGNAL
526 if (sleeppid)
527 kill(sleeppid, SIGTERM);
528 #endif
529 #endif
530
531 #if HAVE_OPEN_C
532 sleep(7);
533 #endif
534
535 return 0;
536 }
537