The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
control_test.c
Go to the documentation of this file.
1 /*
2  * control_test.c Tests for control planes
3  *
4  * Version: $Id: 56090248a9985bfab00f5c40c10640efdb06c25f $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * @copyright 2016 Alan DeKok (aland@freeradius.org)
21  */
22 
23 RCSID("$Id: 56090248a9985bfab00f5c40c10640efdb06c25f $")
24 
25 #include <freeradius-devel/io/control.h>
26 #include <freeradius-devel/util/debug.h>
27 #include <freeradius-devel/util/syserror.h>
28 #include <freeradius-devel/util/talloc.h>
29 #include <freeradius-devel/util/time.h>
30 
31 #include <sys/event.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <pthread.h>
35 
36 #ifdef HAVE_GETOPT_H
37 # include <getopt.h>
38 #endif
39 
40 #undef MEM
41 #define MEM(x) if (!(x)) { fprintf(stderr, "%s[%u] OUT OF MEMORY\n", __FILE__, __LINE__); _exit(EXIT_FAILURE); }
42 #define MPRINT1 if (debug_lvl) printf
43 #define CONTROL_MAGIC 0xabcd6809
44 
45 static int debug_lvl = 0;
46 static int kq = -1;
48 static size_t max_messages = 10;
49 static int aq_size = 16;
50 static fr_control_t *control = NULL;
51 static fr_ring_buffer_t *rb = NULL;
52 
53 /**********************************************************************/
54 typedef struct request_s request_t;
56 void request_verify(UNUSED char const *file, UNUSED int line, UNUSED request_t *request);
57 
59 {
60  return NULL;
61 }
62 
63 void request_verify(UNUSED char const *file, UNUSED int line, UNUSED request_t *request)
64 {
65 }
66 
67 /**********************************************************************/
68 
69 static NEVER_RETURNS void usage(void)
70 {
71  fprintf(stderr, "usage: control_test [OPTS]\n");
72  fprintf(stderr, " -m <messages> Send number of messages.\n");
73  fprintf(stderr, " -x Debugging mode.\n");
74 
75  fr_exit_now(EXIT_SUCCESS);
76 }
77 
78 typedef struct {
80  size_t counter;
81 } my_message_t;
82 
83 static void *control_master(UNUSED void *arg)
84 {
85  TALLOC_CTX *ctx;
86 
87  MEM(ctx = talloc_init_const("control_master"));
88 
89  MPRINT1("Master started.\n");
90 
91  /*
92  * Busy loop. We're stupid.
93  */
94  while (true) {
95  int num_events;
96  ssize_t data_size;
97  my_message_t m;
98  struct kevent kev;
99 
100  wait_for_events:
101  MPRINT1("Master waiting for events.\n");
102 
103  num_events = kevent(kq, NULL, 0, &kev, 1, NULL);
104  if (num_events < 0) {
105  fprintf(stderr, "Failed reading kevent: %s\n", fr_syserror(errno));
106  fr_exit_now(EXIT_FAILURE);
107  }
108 
109  MPRINT1("Master draining the control plane.\n");
110 
111  while (true) {
112  uint32_t id;
113 
114  data_size = fr_control_message_pop(aq, &id, &m, sizeof(m));
115  if (data_size == 0) goto wait_for_events;
116 
117  if (data_size < 0) {
118  fprintf(stderr, "Failed reading control message\n");
119  fr_exit_now(EXIT_FAILURE);
120  }
121 
122  fr_assert(data_size == sizeof(m));
124 
125  MPRINT1("Master got message %zu.\n", m.counter);
126 
128 
129  if (m.counter == (max_messages - 1)) goto do_exit;
130  }
131  }
132 
133 do_exit:
134  MPRINT1("Master exiting.\n");
135 
136  talloc_free(ctx);
137 
138  return NULL;
139 }
140 
141 static void *control_worker(UNUSED void *arg)
142 {
143  size_t i;
144  TALLOC_CTX *ctx;
145 
146  MEM(ctx = talloc_init_const("control_worker"));
147 
148  MPRINT1("\tWorker started.\n");
149 
150  for (i = 0; i < max_messages; i++) {
151  my_message_t m;
152 
153  m.header = CONTROL_MAGIC;
154  m.counter = i;
155 
156 retry:
157  if (fr_control_message_send(control, rb, FR_CONTROL_ID_CHANNEL, &m, sizeof(m)) < 0) {
158  MPRINT1("\tWorker retrying message %zu\n", i);
159  usleep(10);
160  goto retry;
161  }
162 
163  MPRINT1("\tWorker sent message %zu\n", i);
164  }
165 
166  MPRINT1("\tWorker exiting.\n");
167 
168  talloc_free(ctx);
169 
170  return NULL;
171 }
172 
173 
174 
175 int main(int argc, char *argv[])
176 {
177  int c;
178  TALLOC_CTX *autofree = talloc_autofree_context();
179  pthread_attr_t attr;
180  pthread_t master_id, worker_id;
181 
182  fr_time_start();
183 
184  while ((c = getopt(argc, argv, "hm:o:tx")) != -1) switch (c) {
185  case 'x':
186  debug_lvl++;
187  break;
188 
189  case 'm':
190  max_messages = atoi(optarg);
191  break;
192 
193  case 'h':
194  default:
195  usage();
196  }
197 
198 #if 0
199  argc -= (optind - 1);
200  argv += (optind - 1);
201 #endif
202 
203  kq = kqueue();
204  fr_assert(kq >= 0);
205 
207  fr_assert(aq != NULL);
208 
210  if (!control) {
211  fprintf(stderr, "control_test: Failed to create control plane\n");
212  fr_exit_now(EXIT_FAILURE);
213  }
214 
216  if (!rb) fr_exit_now(EXIT_FAILURE);
217 
218  /*
219  * Start the two threads, with the channel.
220  */
221  (void) pthread_attr_init(&attr);
222  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
223 
224  (void) pthread_create(&worker_id, &attr, control_worker, NULL);
225  (void) pthread_create(&master_id, &attr, control_master, NULL);
226 
227  (void) pthread_join(master_id, NULL);
228  (void) pthread_join(worker_id, NULL);
229 
230  close(kq);
231 
232  fr_exit_now(EXIT_SUCCESS);
233 }
int const char * file
Definition: acutest.h:702
va_list args
Definition: acutest.h:770
int const char int line
Definition: acutest.h:702
fr_atomic_queue_t * fr_atomic_queue_alloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
Definition: atomic_queue.c:80
Structure to hold the atomic queue.
Definition: atomic_queue.c:54
#define RCSID(id)
Definition: build.h:481
#define NEVER_RETURNS
Should be placed before the function return type.
Definition: build.h:311
#define UNUSED
Definition: build.h:313
#define FR_CONTROL_ID_CHANNEL
Definition: control.h:56
#define FR_CONTROL_MAX_SIZE
Definition: control.h:51
#define FR_CONTROL_MAX_MESSAGES
Definition: control.h:50
static fr_ring_buffer_t * rb
Definition: control_test.c:51
int main(int argc, char *argv[])
Definition: control_test.c:175
size_t counter
Definition: control_test.c:80
#define CONTROL_MAGIC
Definition: control_test.c:43
static int kq
Definition: control_test.c:46
static int aq_size
Definition: control_test.c:49
static fr_atomic_queue_t * aq
Definition: control_test.c:47
static void * control_worker(UNUSED void *arg)
Definition: control_test.c:141
request_t * request_alloc(UNUSED TALLOC_CTX *ctx, UNUSED request_init_args_t const *args)
Definition: control_test.c:58
uint32_t header
Definition: control_test.c:79
#define MPRINT1
Definition: control_test.c:42
static void * control_master(UNUSED void *arg)
Definition: control_test.c:83
static size_t max_messages
Definition: control_test.c:48
static fr_control_t * control
Definition: control_test.c:50
#define MEM(x)
Definition: control_test.c:41
static NEVER_RETURNS void usage(void)
Definition: control_test.c:69
void request_verify(UNUSED char const *file, UNUSED int line, UNUSED request_t *request)
Definition: control_test.c:63
static int debug_lvl
Definition: control_test.c:45
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition: debug.h:234
ssize_t fr_control_message_pop(fr_atomic_queue_t *aq, uint32_t *p_id, void *data, size_t data_size)
Pop control-plane message.
Definition: control.c:377
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_atomic_queue_t *aq)
Create a control-plane signaling path.
Definition: control.c:149
int fr_control_message_send(fr_control_t *c, fr_ring_buffer_t *rb, uint32_t id, void *data, size_t data_size)
Send a control-plane message.
Definition: control.c:343
The control structure.
Definition: control.c:79
talloc_free(reap)
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
static bool do_exit
Definition: event.c:81
static TALLOC_CTX * autofree
Definition: radclient-ng.c:107
Optional arguments for initialising requests.
Definition: request.h:254
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition: ring_buffer.c:64
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition: schedule.c:151
fr_assert(0)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition: talloc.h:51
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition: talloc.h:112
int fr_time_start(void)
Initialize the local time.
Definition: time.c:150
close(uq->fd)