]> rtime.felk.cvut.cz Git - canping.git/blob - src/canping.c
Link vca_canping with libm
[canping.git] / src / canping.c
1 /*
2     This program is free software: you can redistribute it and/or modify
3     it under the terms of the GNU General Public License as published by
4     the Free Software Foundation, either version 2 of the License, or
5     (at your option) any later version.
6
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10     GNU General Public License for more details.
11
12     You should have received a copy of the GNU General Public License
13     along with this program.  If not, see <http://www.gnu.org/licenses/>.
14
15     Copyright 2005 Michal Sojka <sojkam1@fel.cvut.cz>
16     Copyright 2005-2023 Pavel Pisa <pisa@cmp.felk.cvut.cz>
17 */
18
19 #include <string.h>
20 #include <math.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <pthread.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <ul_list.h>
31 #include <errno.h>
32 #include <semaphore.h>
33
34 /* TODO: Handle the case where there are more canping slaves running
35  * on one CAN bus. */
36
37 #include <can.h>
38
39 //#define DEBUG 1
40 //#define DEBUG 2
41 #define WITH_RTPRIO
42
43 #ifdef WITH_RTPRIO
44 #include <sched.h>
45
46 int sched_policy = SCHED_OTHER;
47 int sched_rtprio;
48
49 #endif
50
51
52 #ifndef DEBUG
53 #define dbg(level, fmt, arg...) do {} while (0)
54 #else
55 #define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0)
56 #endif
57
58 #define NOT_INTERRUPTED_SYSCALL (errno != EINTR && errno != ERESTART)
59 #define IS_FINISH_FLAG() (finish_flag)
60
61 /* Exit codes */
62 #define EXIT_OK 0
63 #define EXIT_BAD_PARAM 1
64 #define EXIT_CANNOT_OPEN 2
65 #define EXIT_FILTER_ERROR 3
66 #define EXIT_NO_MEM 4
67 #define EXIT_READ_ERROR 5
68 #define EXIT_WRITE_ERROR 6
69 #define EXIT_SELECT_ERROR 7
70 #define EXIT_FLUSH_ERROR 8
71
72 /* Global variables */
73 sig_atomic_t finish_flag = 0;   /* Threads should terminate. */
74 sem_t finish_sem;               /* Thread signals a termination */
75 int global_canfd;
76
77 int total_count = 0;
78 int total_timeout = 0;
79
80 pthread_mutex_t mut_start = PTHREAD_MUTEX_INITIALIZER;
81 pthread_cond_t cond_start = PTHREAD_COND_INITIALIZER;
82 int start_flag = 0;
83 sem_t ready_sem;                /* Thread is ready for execution */
84
85
86 /* Command line options */
87 char *option_device = "/dev/can0";
88 int option_masters = 0;
89 long int option_first_id = 1000;
90 int option_slaves = 0;
91 int option_verbose = 0;         /* 0 - nothing, 1 - only global
92                                  * statistics, 2 - simple times, 3 -
93                                  * verbose times */
94 int option_length = 8;
95 int option_count = 0;
96 int option_wait_ms = 1000;
97 int option_timeout = 4;
98 int option_open_once = 0;
99 int option_synch_start = 0;
100
101 /* Lists */
102 typedef struct threads {
103         ul_list_head_t head;
104 } threads_t;
105
106 typedef struct thread_data {
107         pthread_t tid;
108         long int canid;
109
110         int count;
111         double mean;            /* mean value of responses */
112         double moment2nd;       /* used to compute variance of
113                                  * responses */
114         int min, max;           /* min/max response times */
115         int timeout;            /* number of timeouts */
116
117         ul_list_node_t node;
118 } thread_data_t;
119
120 UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
121
122 threads_t master_threads;
123 threads_t slave_threads;
124
125 /* Subtract the `struct timeval' values X and Y, storing the result in
126    RESULT.  Return 1 if the difference is negative, otherwise 0.  */
127      
128 int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
129 {
130         /* Perform the carry for the later subtraction by updating Y. */
131         if (x->tv_usec < y->tv_usec) {
132                 int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
133                 y->tv_usec -= 1000000 * nsec;
134                 y->tv_sec += nsec;
135         }
136         if (x->tv_usec - y->tv_usec > 1000000) {
137                 int nsec = (x->tv_usec - y->tv_usec) / 1000000;
138                 y->tv_usec += 1000000 * nsec;
139                 y->tv_sec -= nsec;
140         }
141      
142         /* Compute the time remaining to wait.
143            `tv_usec' is certainly positive. */
144         result->tv_sec = x->tv_sec - y->tv_sec;
145         result->tv_usec = x->tv_usec - y->tv_usec;
146      
147         /* Return 1 if result is negative. */
148         return x->tv_sec < y->tv_sec;
149 }
150
151 void dbg_print_timeval(char *msg, struct timeval *tv)
152 {
153
154         printf("%s sec=%ld usec=%ld\n", msg, tv->tv_sec, tv->tv_usec);
155 }
156 void kill_all_threads(int signum)
157 {
158         thread_data_t *td;
159                 
160         ul_list_for_each(thread_list, &master_threads, td) {
161                 pthread_kill(td->tid, signum);
162         }
163         ul_list_for_each(thread_list, &slave_threads, td) {
164                 pthread_kill(td->tid, signum);
165         }
166 }
167 void term_handler(int signum)
168 {
169         dbg(1, "Thread %p got a signal %d\n", (void *)pthread_self(), signum);
170         if (!IS_FINISH_FLAG()) {
171                 dbg(1, "Terminating threads\n");
172                 finish_flag = 1;
173
174                 kill_all_threads(signum);
175         }
176 }
177
178 void *master_thread(void *arg)
179 {
180         thread_data_t *td = (thread_data_t *)arg;
181         int ping_id = td->canid;
182         int pong_id = ping_id + 1;
183         struct canmsg_t pingmsg, pongmsg;
184         int ping_count = 0;
185         int ret = 0;
186         int canfd;              /* File descriptor of CAN driver. */
187         int i;
188         fd_set rdset;           /* read set for select syscall */
189         struct timeval timeout, pingtime, pongtime;
190         struct canfilt_t canfilt; /* filter for received messages */
191
192         if (!option_open_once) {
193                 /* Open can driver */
194                 if ((canfd = open(option_device, O_RDWR)) < 0) {
195                         perror("open");
196                         fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
197                         exit(EXIT_CANNOT_OPEN); 
198                 }
199         } else {
200                 canfd = global_canfd;
201         }
202         
203         /* setup filtering of received messages */
204         memset(&canfilt, 0, sizeof(canfilt));
205         canfilt.mask = 0xfffffff;
206         canfilt.id = pong_id;   /* pong responces with increased id */
207         ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
208         if(ret<0) {
209                 perror("ioctl CANQUE_FILTER");
210                 exit(EXIT_FILTER_ERROR);
211         }
212         ret = ioctl(canfd, CANQUE_FLUSH, NULL);
213         if(ret<0) {
214                 perror("ioctl CANQUE_QUEUE_FLUSH");
215                 exit(EXIT_FLUSH_ERROR);
216         }
217         
218         /* Prepare data structures for the select syscall. */
219         FD_ZERO (&rdset);
220
221         /* Signal that I'm ready */
222         sem_post(&ready_sem);
223
224         if (option_synch_start) {
225                 /* Wait for other threads to initialize */
226                 pthread_mutex_lock(&mut_start);
227                 while (!start_flag) pthread_cond_wait(&cond_start, &mut_start);
228                 pthread_mutex_unlock(&mut_start);
229         }
230
231         while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
232                 /* Send a ping message */
233                 pingmsg.flags=0;
234                 pingmsg.id=ping_id;
235                 pingmsg.length = option_length;
236                 for (i=0; i < option_length; i++) pingmsg.data[i] = i;
237                 gettimeofday(&pingtime, NULL);
238
239                 ret = write(canfd, &pingmsg, sizeof(pingmsg));
240                 if (ret < 0) {
241                         if (NOT_INTERRUPTED_SYSCALL) {
242                                 perror("write");
243                                 exit(EXIT_WRITE_ERROR);
244                         }
245                         else continue;
246                 }
247                 
248                 /* Wait for a pong responce */
249                 FD_SET (canfd, &rdset);
250      
251                 timeout.tv_sec = option_timeout;
252                 timeout.tv_usec = 0;
253      
254                 ret = select(FD_SETSIZE, &rdset, NULL, NULL, &timeout);
255                 if (ret > 0) {
256                         /* Read the message */
257                         pongmsg.flags=0;
258                         ret = read(canfd, &pongmsg, sizeof(pongmsg));
259                         if (ret < 0) {
260                                 if (NOT_INTERRUPTED_SYSCALL) {
261                                         perror("read");
262                                         exit(EXIT_READ_ERROR);
263                                 }
264                                 else continue;
265                         }
266                         else {  /* A pong message received */
267                                 long int time;
268                                 gettimeofday(&pongtime, NULL);
269                                 if (ret == 0) {
270                                         fprintf(stderr, "read returned zero\n");
271                                         exit(EXIT_READ_ERROR);
272                                 }
273                                 if (pongmsg.id != pong_id) {
274                                         fprintf(stderr, "Filter error (expected: %d, received %ld)\n", pong_id, pongmsg.id);
275                                         exit(EXIT_FILTER_ERROR);
276                                 }
277                                 timeval_subtract(&pongtime, &pongtime, &pingtime);
278                                 time = pongtime.tv_sec*1000000 + pongtime.tv_usec;
279                                 switch (option_verbose) {
280                                 case 2:
281                                         printf("%d:%ld\n", ping_id, time); 
282                                         break;
283                                 case 3:
284                                         printf("Pong response for id %d received in %ld us\n", ping_id, time);
285                                         break;
286                                 }
287                                 /* Update statistics */
288                                 td->count++;
289                                 td->mean = 
290                                         td->mean * ((double)(td->count - 1) / td->count) + 
291                                         (double)time / td->count;
292                                 td->moment2nd = 
293                                         td->moment2nd * ((double)(td->count - 1) / td->count) + 
294                                         (double)time*time  / td->count;
295                                 if (time > td->max) td->max = time;
296                                 if (time < td->min) td->min = time;
297                                 total_count++;
298                         } /* read */
299                 } 
300                 else if (ret == 0) { /* select */
301                         if (option_verbose >= 2) 
302                                 printf("Timeout encountered (id %d)\n", ping_id);
303                         td->timeout++;
304                         total_timeout++;
305                 } 
306                 else {
307                         if (NOT_INTERRUPTED_SYSCALL) {
308                                 perror("select");
309                                 exit(EXIT_SELECT_ERROR);
310                         }
311                         else continue;
312                 }
313                 usleep(option_wait_ms * 1000);
314         }
315
316         if (!option_open_once) {
317                 /* Close the can driver */
318                 close(canfd);
319         }
320
321         sem_post(&finish_sem);
322         return (void *)ret;
323 }
324
325 void start_masters(int masters, int first_id)
326 {
327         int id = first_id;
328         int i;
329
330         dbg(1, "Starting %d master threads\n", masters);
331         
332         for (i = 0; i < masters; i++, id += 2) {
333                 thread_data_t *td;
334                 
335                 td = malloc(sizeof(*td));
336                 if (!td) {
337                         printf("Can't allocate memory");
338                         exit(EXIT_NO_MEM);
339                 }
340                 memset(td, 0, sizeof(*td));
341                 td->canid = id;
342                 td->min = 0x7fffffff;
343                 /* TODO use mutexes and signal blocking */
344                 thread_list_ins_tail(&master_threads, td);
345                 pthread_create(&td->tid, NULL, master_thread, (void *)td);
346                 dbg(2, "Master thread: %p\n", (void *)td->tid);
347         }
348
349
350         /* Wait for all threads beeing ready */
351         for (i = 0; i < masters; i++) sem_wait(&ready_sem);
352
353         /* Start threads */
354         pthread_mutex_lock(&mut_start);
355         start_flag = 1;
356         pthread_cond_broadcast(&cond_start);
357         pthread_mutex_unlock(&mut_start);
358 }
359
360 void *slave_thread(void *arg)
361 {
362         thread_data_t *td = (thread_data_t *)arg;
363         int ping_id = td->canid;
364         int pong_id = ping_id + 1;
365         struct canmsg_t pingmsg, pongmsg;
366         int ret = 0;
367         int canfd;              /* File descriptor of CAN driver. */
368         int i;
369         struct canfilt_t canfilt; /* filter for received messages */
370
371         if (!option_open_once) {
372                 /* Open the CAN driver */
373                 if ((canfd = open(option_device, O_RDWR)) < 0) {
374                         perror("open");
375                         printf("Error opening %s (for id %d)\n", option_device, ping_id);
376                         exit(EXIT_CANNOT_OPEN); 
377                 }
378         } else {
379                 canfd = global_canfd;
380         }
381         
382         /* setup filtering of received messages */
383         memset(&canfilt, 0, sizeof(canfilt));
384         canfilt.mask = 0xfffffff;
385         canfilt.id = ping_id;   /* receive only our ping messages */
386         ret = ioctl(canfd, CANQUE_FILTER, &canfilt);
387         if (ret < 0) {
388                 perror("ioctl CANQUE_FILTER");
389                 exit(EXIT_FILTER_ERROR);
390         }
391         /* If there are some messages already, delete them. These may
392          * not processed by our filter and thus may have a wrong
393          * ID. */
394         ret = ioctl(canfd, CANQUE_FLUSH, NULL);
395         if (ret < 0) {
396                 perror("ioctl CANQUE_QUEUE_FLUSH");
397                 exit(EXIT_FLUSH_ERROR);
398         }
399         
400         /* Prepare a pong message */
401         pongmsg.flags = 0;
402         pongmsg.id = pong_id;
403         pongmsg.length = option_length;
404         for (i=0; i < option_length; i++) pongmsg.data[i] = i;
405
406         while (!IS_FINISH_FLAG()) {
407                 /* Receive a ping message */
408                 pingmsg.flags=0;
409                 ret = read(canfd, &pingmsg, sizeof(pingmsg));
410                 if (ret < 0) {
411                         if (NOT_INTERRUPTED_SYSCALL) {
412                                 printf("%d\n", errno);
413                                 perror("read");
414                                 exit(EXIT_READ_ERROR);
415                         }
416                 }
417                 if (NOT_INTERRUPTED_SYSCALL && pingmsg.id != ping_id) {
418                         fprintf(stderr, "Filter error (expected: %d, received %ld)\n", ping_id, pingmsg.id);
419                         exit(EXIT_FILTER_ERROR);
420                 }
421                 /* Answer immendiately with a pong message */
422                 if (NOT_INTERRUPTED_SYSCALL) {
423                         ret = write(canfd, &pongmsg, sizeof(pongmsg));
424                         if (ret < 0) {
425                                 if (NOT_INTERRUPTED_SYSCALL) {
426                                         perror("write");
427                                         exit(EXIT_WRITE_ERROR);
428                                 }
429                         }
430                 }
431
432                 if (ret >= 0) total_count++;
433
434                 if (option_verbose >= 2)
435                         /* This drasticly slows down the pong
436                          * response. Why??? */
437                         printf("Replying to ping id %lu\n", pingmsg.id);
438
439         }
440
441         if (!option_open_once) {
442                 /* Close can driver */
443                 close(canfd);
444         }
445
446         dbg(2, "Slave thread for id %d is going to finish\n", ping_id);
447
448         sem_post(&finish_sem);
449         return (void *)ret;
450 }
451      
452 void start_slaves(int slaves, int first_id)
453 {
454         int id = first_id;
455         int i;
456
457         dbg(1, "Starting %d slave threads\n", slaves);
458         
459         for (i = 0; i < slaves; i++, id += 2) {
460                 thread_data_t *td;
461                 
462                 td = malloc(sizeof(*td));
463                 if (!td) {
464                         printf("Can't allocate memory");
465                         exit(EXIT_NO_MEM);
466                 }
467                 memset(td, 0, sizeof(*td));
468                 td->canid = id;
469                 /* TODO use mutexes and signal blocking */
470                 thread_list_ins_tail(&slave_threads, td);
471                 pthread_create(&td->tid, NULL, slave_thread, (void *)td);
472                 dbg(2, "Slave thread: %p\n", (void *)td->tid);
473         }
474 }
475
476 #ifdef WITH_RTPRIO
477 int set_sched_policy_and_prio(int policy, int rtprio)
478 {
479         struct sched_param scheduling_parameters;
480         int maxprio=sched_get_priority_max(policy);
481         int minprio=sched_get_priority_min(policy);
482
483         if((rtprio < minprio) || (rtprio > maxprio)) {
484                 fprintf(stderr, "The priority for requested policy is out of <%d, %d> range\n",
485                                 minprio, maxprio);
486                 return -1;
487         }
488
489         scheduling_parameters.sched_priority = rtprio;
490
491         if (0 != pthread_setschedparam(pthread_self(), policy, &scheduling_parameters)) {
492                 perror("pthread_setschedparam error");
493         }
494         return 0;
495 }
496 #endif
497
498 void print_help(void)
499 {
500         printf("Usage: canping -m <master threads> [other options]\n"
501                "       canping -s <slave threads> [other options]\n\n"
502                "Other options:\n"
503                "  -c count      how many messages each master sends\n"
504                "  -d dev        device (e.g. /dev/can1)\n"
505                "  -h            print this help\n"
506                "  -i id         id of first master message\n"
507                "  -l length     length of the messages (0..8)\n"
508                "  -o            open a device only once for all threads (doesn't work)\n" /* due to filters */
509                "  -t timeout    timeout in seconds (default 4 s)\n"
510                "  -v            be verbose (use more than once to increase verbosity)\n"
511                "  -w ms         wait ms miliseconds between sending pings\n"
512                "  -y            synchronize threads before start (doesn't test race conditions\n"
513                "                between open and read/write)\n"
514 #ifdef WITH_RTPRIO
515                "  -r            run at realtime priority\n"
516                "  -R P:prio     run at realtime priority prio, policy RR or FF\n"
517 #endif
518                "\n"
519                "Example: canping -m 10 -vv\n"
520                 );
521 }
522      
523 int parse_options(int argc, char *argv[])
524 {
525         int c;
526         
527         opterr = 0;
528         while ((c = getopt (argc, argv, "c:d:hi:l:m:os:t:vw:yrR:")) != -1)
529                 switch (c)
530                 {
531                 case 'c':
532                         option_count = atoi(optarg);
533                         break;
534                 case 'd':
535                         option_device = optarg;
536                         break;
537                 case 'h':
538                         print_help();
539                         exit(EXIT_OK);
540                         break;
541                 case 'i':
542                         option_first_id = atoi(optarg);
543                         break;
544                 case 'l':
545                         option_length = atoi(optarg);
546                         if (option_length > 8) option_length = 8;
547                         if (option_length < 0) option_length = 0;
548                         break;
549                 case 'm':
550                         option_masters = atoi(optarg);
551                         break;
552                 case 'o':
553                         option_open_once = 1;
554                         break;
555                 case 's':
556                         option_slaves = atoi(optarg);
557                         break;
558                 case 't':
559                         option_timeout = atoi(optarg);
560                         break;
561                 case 'v':
562                         option_verbose++;
563                         break;
564                 case 'w':
565                         option_wait_ms = atoi(optarg);
566                         break;
567                 case 'y':
568                         option_synch_start = 1;
569                         break;
570 #ifdef WITH_RTPRIO
571                 case 'r':
572                         sched_policy = SCHED_FIFO;
573                         sched_rtprio = (sched_get_priority_min(SCHED_FIFO) +
574                                         sched_get_priority_max(SCHED_FIFO)) / 2;
575                         break;
576                 case 'R':
577                         if(!isalpha(*optarg)) {
578                                 sched_policy = SCHED_FIFO;
579                         } else if(!strncmp(optarg,"FF:",3)) {
580                                 sched_policy = SCHED_FIFO;
581                                 optarg += 3;
582                         } else if(!strncmp(optarg,"RR:",3)) {
583                                 sched_policy = SCHED_RR;
584                                 optarg += 3;
585                         } else {
586                                 fprintf (stderr, "Unknown policy %s\n", optarg);
587                                 exit(EXIT_BAD_PARAM);
588                         }
589                         sched_rtprio = atoi(optarg);
590                         break;
591 #endif
592                 case '?':
593                         if (isprint (optopt))
594                                 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
595                         else
596                                 fprintf (stderr,
597                                          "Unknown option character `\\x%x'.\n",
598                                          optopt);
599                         return 1;
600                 default:
601                         exit(EXIT_BAD_PARAM);
602                 }
603         if (!(option_masters || option_slaves) || (option_masters && option_slaves))
604                 exit(EXIT_BAD_PARAM);
605         return 0;
606 }
607
608 void print_stats(thread_data_t *td)
609 {
610         char std[20];
611         int count = td->count + td->timeout;
612         
613         if (td->count >= 2) {
614                 snprintf(std, 19, "%7.2f", sqrt((td->count/(td->count - 1)) * 
615                                                 (td->moment2nd - td->mean*td->mean)));
616         } else {
617                 strncpy(std, "N/A", 19);
618         }
619         printf("Id %4ld: count = %5d"
620                " mean = %7.2f stddev = %s"
621                " min = %5d max = %5d [us]"
622                " loss = %d%% (%d)\n",
623                td->canid, count, td->mean, 
624                std,
625                td->min, td->max,
626                (count > 0) ? 100 * td->timeout / count : 0, td->timeout
627                 );
628 }
629
630 void wait_for_threads(void)
631 {
632         thread_data_t *td;
633         void *thread_ret;
634         int thread_count = option_slaves + option_masters;
635         int ret;
636
637         while (thread_count > 0) {
638                 if (option_verbose != 1) {
639                         ret = sem_wait(&finish_sem);
640                         dbg(2, "Main thread sem_wait() exited with ret=%d.\n", ret);
641
642                         /* If the sem_wait is successful i.e. not
643                          * interrupted by a signal, decrease the
644                          * thread_count*/
645                         if (ret == 0) thread_count--; 
646                 } else {
647                         if (!IS_FINISH_FLAG()) {
648                                 printf("Total count: %6d, Timeouts: %6d\r", total_count, total_timeout);
649                                 fflush(stdout);
650                                 usleep(1000000);
651                         }
652                         while (sem_trywait(&finish_sem) == 0) thread_count--;
653                 }
654         }
655         if (option_verbose == 1) {
656                 printf("\n");
657         }
658
659         ul_list_for_each(thread_list, &master_threads, td) {
660                 pthread_join(td->tid, &thread_ret);
661                 dbg(2, "Master thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
662         }
663
664         ul_list_for_each(thread_list, &slave_threads, td) {
665                 pthread_join(td->tid, &thread_ret);
666                 dbg(2, "Slave thread for id %ld finished; exit code %d\n", td->canid, (int)thread_ret);
667         }
668
669         if (option_masters) printf("Summary statistics:\n");
670
671         ul_list_for_each_cut(thread_list, &master_threads, td) {
672                 print_stats(td);
673                 free(td);
674         }
675         
676
677         ul_list_for_each_cut(thread_list, &slave_threads, td) 
678                 free(td);
679 }
680
681 int main(int argc, char *argv[])
682 {
683         parse_options(argc, argv);
684
685 #ifdef WITH_RTPRIO
686         if(sched_policy != SCHED_OTHER)
687                 if(set_sched_policy_and_prio(sched_policy, sched_rtprio) <0)
688                         exit(EXIT_BAD_PARAM);
689 #endif
690
691
692         thread_list_init_head(&master_threads);
693         thread_list_init_head(&slave_threads);
694
695         sem_init(&finish_sem, 0, 0);
696         sem_init(&ready_sem, 0, 0);
697
698         siginterrupt(SIGINT, 1);
699         signal(SIGINT, term_handler);
700         siginterrupt(SIGTERM, 1);
701         signal(SIGTERM, term_handler);
702
703         dbg(2, "Main thread: %p\n", (void *)pthread_self());
704
705         if (option_open_once) {
706                 /* Open the CAN driver */
707                 if ((global_canfd = open(option_device, O_RDWR)) < 0) {
708                         perror("open");
709                         printf("Error opening %s\n", option_device);
710                         exit(EXIT_CANNOT_OPEN); 
711                 }
712         }
713
714         if (option_masters) start_masters(option_masters, option_first_id);
715         if (option_slaves) start_slaves(option_slaves, option_first_id);
716
717         wait_for_threads();
718
719         if (option_open_once) {
720                 close(global_canfd);
721         }
722
723         return 0;
724 }