Computer Laboratory

tesla_notification.c
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 2013 Jonathan Anderson
3  * All rights reserved.
4  *
5  * This software was developed by SRI International and the University of
6  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7  * ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id$
31  */
32 
33 #include "tesla_internal.h"
34 
35 #define ERROR_BUFFER_LENGTH 1024
36 
40 static struct tesla_event_metahandler *event_handlers;
41 
42 
44 static int
45 check_event_handler(const struct tesla_event_handlers *tehp)
46 {
47 
48  if (!tehp || !tehp->teh_init || !tehp->teh_transition
49  || !tehp->teh_clone || !tehp->teh_fail_no_instance
50  || !tehp->teh_bad_transition || !tehp->teh_err
51  || !tehp->teh_accept || !tehp->teh_ignored)
52  return (TESLA_ERROR_EINVAL);
53 
54  return (TESLA_SUCCESS);
55 }
56 
57 
58 int
60 {
61  int error = check_event_handler(tehp);
62  if (error != TESLA_SUCCESS)
63  return (error);
64 
65  const static struct tesla_event_handlers* singleton[1];
66  static struct tesla_event_metahandler singleton_handler = {
67  .tem_length = 1,
68  .tem_mask = 1,
69  .tem_handlers = singleton,
70  };
71 
72  singleton[0] = tehp;
73  event_handlers = &singleton_handler;
74 
75  return (TESLA_SUCCESS);
76 }
77 
78 int
80 {
81  int error = TESLA_SUCCESS;
82 
83  if (!temp)
84  return (TESLA_ERROR_EINVAL);
85 
86  /*
87  * It's ok to disable event handlers dynamically using the bitmask,
88  * but all event handlers passed in must be valid.
89  */
90  for (uint32_t i = 0; i < temp->tem_length; i++) {
91  error = check_event_handler(temp->tem_handlers[i]);
92  if (error != TESLA_SUCCESS)
93  return (error);
94  }
95 
96  event_handlers = temp;
97  return (TESLA_SUCCESS);
98 }
99 
100 
101 /*
102  * generic event handlers:
103  */
104 #define FOREACH_ERROR_HANDLER() \
105  for (uint32_t i = 0; i < event_handlers->tem_length; i++) \
106  if (event_handlers->tem_mask & (1 << i)) \
107  event_handlers->tem_handlers[i]
108 
109 static void
110 ev_noop()
111 {
112 }
113 
114 void
115 ev_new_instance(struct tesla_class *tcp, struct tesla_instance *tip)
116 {
117 
118  FOREACH_ERROR_HANDLER()->teh_init(tcp, tip);
119 }
120 
121 void
122 ev_transition(struct tesla_class *tcp, struct tesla_instance *tip,
123  const struct tesla_transition *ttp)
124 {
125 
126  FOREACH_ERROR_HANDLER()->teh_transition(tcp, tip, ttp);
127 }
128 
129 void
130 ev_clone(struct tesla_class *tcp, struct tesla_instance *orig,
131  struct tesla_instance *copy, const struct tesla_transition *ttp)
132 {
133 
134  FOREACH_ERROR_HANDLER()->teh_clone(tcp, orig, copy, ttp);
135 }
136 
137 void
138 ev_no_instance(struct tesla_class *tcp, const struct tesla_key *tkp,
139  const struct tesla_transitions *ttp)
140 {
141 
142  FOREACH_ERROR_HANDLER()->teh_fail_no_instance(tcp, tkp, ttp);
143 }
144 
145 void
147  const struct tesla_transitions *ttp)
148 {
149 
150  FOREACH_ERROR_HANDLER()->teh_bad_transition(tcp, tip, ttp);
151 }
152 
153 void
154 ev_err(struct tesla_class *tcp, int errno, const char *message)
155 {
156 
157  FOREACH_ERROR_HANDLER()->teh_err(tcp, errno, message);
158 }
159 
160 void
161 ev_accept(struct tesla_class *tcp, struct tesla_instance *tip)
162 {
163 
164  FOREACH_ERROR_HANDLER()->teh_accept(tcp, tip);
165 }
166 
167 void
168 ev_ignored(const struct tesla_class *tcp, const struct tesla_key *tkp,
169  const struct tesla_transitions *ttp)
170 {
171 
172  FOREACH_ERROR_HANDLER()->teh_ignored(tcp, tkp, ttp);
173 }
174 
175 
176 /*
177  * printf()-based event handlers:
178  */
179 static void
180 print_failure_header(const struct tesla_class *tcp)
181 {
182 
183  error("\n\nTESLA failure:\n");
184 #if defined(_KERNEL) && defined(KDB)
185  kdb_backtrace();
186 #endif
187 
188  error("In automaton '%s':\n%s\n", tcp->tc_name, tcp->tc_description);
189 }
190 
191 static void
192 print_new_instance(struct tesla_class *tcp, struct tesla_instance *tip)
193 {
194 
195  DEBUG(libtesla.instance.new, "new %td: %d\n",
196  tip - tcp->tc_instances, tip->ti_state);
197 }
198 
199 static void
200 print_transition_taken(struct tesla_class *tcp,
201  struct tesla_instance *tip, const struct tesla_transition *transp)
202 {
203 
204  DEBUG(libtesla.state.transition, "update %td: %d->%d\n",
205  tip - tcp->tc_instances, transp->from, transp->to);
206 }
207 
208 static void
209 print_clone(struct tesla_class *tcp,
210  struct tesla_instance *old_instance, struct tesla_instance *new_instance,
211  const struct tesla_transition *transp)
212 {
213 
214  DEBUG(libtesla.instance.clone, "clone %td:%d -> %td:%d\n",
215  old_instance - tcp->tc_instances, transp->from,
216  new_instance - tcp->tc_instances, transp->to);
217 }
218 
219 static void
220 print_no_instance(struct tesla_class *tcp, const struct tesla_key *tkp,
221  const struct tesla_transitions *transp)
222 {
223 
224  assert(tcp != NULL);
225  assert(tkp != NULL);
226 
227  print_failure_header(tcp);
228 
229  char buffer[ERROR_BUFFER_LENGTH];
230  const char *end = buffer + sizeof(buffer);
231  char *next = buffer;
232 
233  SAFE_SPRINTF(next, end, "No instance matched key '");
234  next = key_string(next, end, tkp);
235  SAFE_SPRINTF(next, end, "' for transition(s) ");
236  next = sprint_transitions(next, end, transp);
237  assert(next > buffer);
238 
239  error("%s", buffer);
240 }
241 
242 static void
243 print_bad_transition(struct tesla_class *tcp, struct tesla_instance *tip,
244  const struct tesla_transitions *transp)
245 {
246 
247  assert(tcp != NULL);
248  assert(tip != NULL);
249 
250  print_failure_header(tcp);
251 
252  char buffer[ERROR_BUFFER_LENGTH];
253  const char *end = buffer + sizeof(buffer);
254  char *next = buffer;
255 
256  SAFE_SPRINTF(next, end,
257  "Instance %td is in state %d\n"
258  "but required to take a transition in ",
259  (tip - tcp->tc_instances), tip->ti_state);
260  assert(next > buffer);
261 
262  next = sprint_transitions(next, end, transp);
263  assert(next > buffer);
264 
265  error("%s", buffer);
266 }
267 
268 static void
269 print_error(struct tesla_class *tcp, int errno, const char *message)
270 {
271 
272  DEBUG(libtesla.event, "%s in '%s': %s\n",
273  tesla_strerror(errno), tcp->tc_name, message);
274 }
275 
276 static void
277 print_accept(struct tesla_class *tcp, struct tesla_instance *tip)
278 {
279 
280  DEBUG(libtesla.instance.success,
281  "pass '%s': %td\n", tcp->tc_name,
282  tip - tcp->tc_instances);
283 }
284 
285 static void
286 print_ignored(const struct tesla_class *tcp, const struct tesla_key *tkp,
287  const struct tesla_transitions *transp)
288 {
289  char buffer[ERROR_BUFFER_LENGTH];
290  char *next = buffer;
291  const char *end = buffer + sizeof(buffer);
292 
293  next = key_string(next, end, tkp);
294  SAFE_SPRINTF(next, end, " : ");
295  sprint_transitions(next, end, transp);
296 
297  DEBUG(libtesla.event, "ignore '%s':%s", tcp->tc_name, buffer);
298 }
299 
300 static const struct tesla_event_handlers printf_handlers = {
301  .teh_init = print_new_instance,
302  .teh_transition = print_transition_taken,
303  .teh_clone = print_clone,
304  .teh_fail_no_instance = print_no_instance,
305  .teh_bad_transition = print_bad_transition,
306  .teh_err = print_error,
307  .teh_accept = print_accept,
308  .teh_ignored = print_ignored,
309 };
310 
311 static const struct tesla_event_handlers printf_on_failure = {
312  .teh_init = ev_noop,
313  .teh_transition = ev_noop,
314  .teh_clone = ev_noop,
315  .teh_fail_no_instance = print_no_instance,
316  .teh_bad_transition = print_bad_transition,
317  .teh_err = print_error,
318  .teh_accept = ev_noop,
319  .teh_ignored = ev_noop,
320 };
321 
322 /*
323  * Wrappers that panic on failure:
324  */
325 static void
326 panic_no_instance(struct tesla_class *tcp,
327  __unused const struct tesla_key *tkp,
328  __unused const struct tesla_transitions *ttp)
329 {
330 
331  tesla_panic("TESLA: failure in '%s': no such instance", tcp->tc_name);
332 }
333 
334 static void
335 panic_bad_transition(struct tesla_class *tcp,
336  __unused struct tesla_instance *tip,
337  __unused const struct tesla_transitions *ttp)
338 {
339 
340  tesla_panic("TESLA: failure in '%s': bad transition", tcp->tc_name);
341 }
342 
343 static const struct tesla_event_handlers failstop_handlers = {
344  .teh_init = ev_noop,
345  .teh_transition = ev_noop,
346  .teh_clone = ev_noop,
347  .teh_fail_no_instance = panic_no_instance,
348  .teh_bad_transition = panic_bad_transition,
349  .teh_accept = ev_noop,
350  .teh_ignored = ev_noop,
351 };
352 
353 
358 const static struct tesla_event_handlers* const default_handlers[] = {
359  &printf_handlers,
360  &printf_on_failure,
361 #if defined(_KERNEL) && defined(KDTRACE_HOOKS)
362  &dtrace_handlers,
363 #endif
364  &failstop_handlers,
365 };
366 
367 static struct tesla_event_metahandler default_event_handlers = {
368  .tem_length = sizeof(default_handlers) / sizeof(*default_handlers),
369 #if defined(_KERNEL) && defined(KDTRACE_HOOKS)
370  .tem_mask = TESLA_KERN_DTRACE_EV,
371 #else
372  .tem_mask = 0x5,
373 #endif
374  .tem_handlers = default_handlers,
375 };
376 
377 #ifdef _KERNEL
378 #include <sys/sysctl.h>
379 
380 SYSCTL_NODE(, OID_AUTO, tesla, CTLFLAG_RW, 0, "TESLA");
381 SYSCTL_NODE(_tesla, OID_AUTO, events, CTLFLAG_RW, 0, "control of TESLA events");
382 SYSCTL_UINT(_tesla_events, OID_AUTO, handlers, CTLFLAG_RW,
383  &default_event_handlers.tem_mask, 0,
384  "Mask of currently-enabled TESLA event handlers");
385 #endif
386 
387 static struct tesla_event_metahandler *event_handlers = &default_event_handlers;