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