1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * The "program" executed by the injector consists of a tree of commands.
31  * Routines in this file build and execute said command tree.
32  */
33 
34 #include <sys/fm/protocol.h>
35 #include <unistd.h>
36 
37 #include <inj.h>
38 #include <inj_event.h>
39 #include <inj_lex.h>
40 #include <inj_err.h>
41 
42 /*
43  * Command tree construction
44  */
45 
46 static inj_list_t inj_cmds;
47 
48 void
49 inj_cmds_add(inj_cmd_t *cmd)
50 {
51 	inj_list_append(&inj_cmds, cmd);
52 }
53 
54 inj_list_t *
55 inj_cmds_get(void)
56 {
57 	return (&inj_cmds);
58 }
59 
60 inj_randelem_t *
61 inj_rand_create(inj_defn_t *ev, uint_t prob)
62 {
63 	inj_randelem_t *re = inj_zalloc(sizeof (inj_randelem_t));
64 
65 	re->re_event = ev;
66 	re->re_prob = prob;
67 
68 	return (re);
69 }
70 
71 inj_randelem_t *
72 inj_rand_add(inj_randelem_t *list, inj_randelem_t *new)
73 {
74 	new->re_next = list;
75 	return (new);
76 }
77 
78 inj_cmd_t *
79 inj_cmd_rand(inj_randelem_t *rlist)
80 {
81 	inj_randelem_t *r;
82 	inj_cmd_t *cmd;
83 	uint_t prob, tmpprob;
84 	int nelems, i;
85 
86 	prob = 0;
87 	for (i = 0, r = rlist; r != NULL; r = r->re_next, i++)
88 		prob += r->re_prob;
89 
90 	if (prob != 100) {
91 		yyerror("probabilities don't sum to 100\n");
92 		return (NULL);
93 	}
94 
95 	nelems = i;
96 
97 	cmd = inj_zalloc(sizeof (inj_cmd_t));
98 	cmd->cmd_type = CMD_RANDOM;
99 	cmd->cmd_num = nelems;
100 	cmd->cmd_rand = inj_alloc(sizeof (inj_randelem_t *) * nelems);
101 
102 	prob = 0;
103 	for (r = rlist, i = 0; i < nelems; i++, r = r->re_next) {
104 		tmpprob = r->re_prob;
105 		r->re_prob = prob;
106 		prob += tmpprob;
107 
108 		cmd->cmd_rand[i] = r;
109 	}
110 
111 	return (cmd);
112 }
113 
114 inj_cmd_t *
115 inj_cmd_repeat(inj_cmd_t *repcmd, uint_t num)
116 {
117 	inj_cmd_t *cmd = inj_zalloc(sizeof (inj_cmd_t));
118 
119 	cmd->cmd_type = CMD_REPEAT;
120 	cmd->cmd_num = num;
121 	cmd->cmd_subcmd = repcmd;
122 
123 	return (cmd);
124 }
125 
126 inj_cmd_t *
127 inj_cmd_send(inj_defn_t *ev)
128 {
129 	inj_cmd_t *cmd = inj_zalloc(sizeof (inj_cmd_t));
130 
131 	cmd->cmd_type = CMD_SEND_EVENT;
132 	cmd->cmd_event = ev;
133 
134 	return (cmd);
135 }
136 
137 inj_cmd_t *
138 inj_cmd_sleep(uint_t secs)
139 {
140 	inj_cmd_t *cmd = inj_zalloc(sizeof (inj_cmd_t));
141 
142 	cmd->cmd_type = CMD_SLEEP;
143 	cmd->cmd_num = secs;
144 
145 	return (cmd);
146 }
147 
148 inj_cmd_t *
149 inj_cmd_addhrt(hrtime_t delta)
150 {
151 	const char *class = "resource.sunos.fmd.clock.addhrtime";
152 	inj_cmd_t *cmd = inj_zalloc(sizeof (inj_cmd_t));
153 	inj_defn_t *ev = inj_zalloc(sizeof (inj_defn_t));
154 
155 	ev->defn_name = class;
156 	ev->defn_lineno = yylineno;
157 
158 	if ((errno = nvlist_alloc(&ev->defn_nvl, NV_UNIQUE_NAME, 0)) != 0)
159 		die("failed to allocate nvl for %s event", class);
160 
161 	if ((errno = nvlist_add_string(ev->defn_nvl, FM_CLASS, class)) != 0 ||
162 	    (errno = nvlist_add_uint8(ev->defn_nvl, FM_VERSION, 1)) != 0 ||
163 	    (errno = nvlist_add_int64(ev->defn_nvl, "delta", delta)) != 0)
164 		die("failed to build nvl for %s event", class);
165 
166 	cmd->cmd_type = CMD_SEND_EVENT;
167 	cmd->cmd_event = ev;
168 
169 	return (cmd);
170 }
171 
172 inj_cmd_t *
173 inj_cmd_endhrt(void)
174 {
175 	return (inj_cmd_addhrt(-1LL)); /* clock underflow causes end of time */
176 }
177 
178 static uint64_t
179 inj_ena(void)
180 {
181 	return (((gethrtime() & ENA_FMT1_TIME_MASK) <<
182 	    ENA_FMT1_TIME_SHFT) | (FM_ENA_FMT1 & ENA_FORMAT_MASK));
183 }
184 
185 static void
186 cmd_run_send(const inj_mode_ops_t *mode, void *hdl, inj_defn_t *ev)
187 {
188 	if (!quiet) {
189 		(void) printf("sending event %s ... ", ev->defn_name);
190 		(void) fflush(stdout);
191 	}
192 
193 	if (ev->defn_decl && (ev->defn_decl->decl_flags & DECL_F_AUTOENA) &&
194 	    (errno = nvlist_add_uint64(ev->defn_nvl, "ena", inj_ena())) != 0)
195 		warn("failed to add ena to %s", ev->defn_name);
196 
197 	if (verbose) {
198 		nvlist_print(stdout, ev->defn_nvl);
199 		(void) printf("\n");
200 	}
201 
202 	mode->mo_send(hdl, ev->defn_nvl);
203 
204 	if (!quiet)
205 		(void) printf("done\n");
206 }
207 
208 static void
209 cmd_run_random(const inj_mode_ops_t *mode, void *hdl, inj_cmd_t *cmd)
210 {
211 	uint_t num = lrand48() % 100;
212 	int i;
213 
214 	for (i = 1; i < cmd->cmd_num; i++) {
215 		if (cmd->cmd_rand[i]->re_prob > num)
216 			break;
217 	}
218 
219 	cmd_run_send(mode, hdl, cmd->cmd_rand[i - 1]->re_event);
220 }
221 
222 static void
223 cmd_run(const inj_mode_ops_t *mode, void *hdl, inj_cmd_t *cmd)
224 {
225 	switch (cmd->cmd_type) {
226 	case CMD_SEND_EVENT:
227 		cmd_run_send(mode, hdl, cmd->cmd_event);
228 		break;
229 
230 	case CMD_SLEEP:
231 		(void) printf("sleeping for %d sec%s ... ",
232 		    cmd->cmd_num, cmd->cmd_num > 1 ? "s" : "");
233 		(void) fflush(stdout);
234 		(void) sleep(cmd->cmd_num);
235 		(void) printf("done\n");
236 		break;
237 
238 	case CMD_RANDOM:
239 		cmd_run_random(mode, hdl, cmd);
240 		break;
241 
242 	default:
243 		warn("ignoring unknown command type: %d\n", cmd->cmd_type);
244 	}
245 }
246 
247 void
248 inj_program_run(inj_list_t *prog, const inj_mode_ops_t *mode, void *mode_arg)
249 {
250 	void *hdl = mode->mo_open(mode_arg);
251 	inj_cmd_t *cmd;
252 	int i;
253 
254 	for (cmd = inj_list_next(prog); cmd != NULL; cmd = inj_list_next(cmd)) {
255 		if (cmd->cmd_type == CMD_REPEAT) {
256 			for (i = 1; i <= cmd->cmd_num; i++) {
257 				if (verbose) {
258 					(void) printf("(repeat %d of %d)\n",
259 					    i, cmd->cmd_num);
260 				}
261 				cmd_run(mode, hdl, cmd->cmd_subcmd);
262 			}
263 		} else
264 			cmd_run(mode, hdl, cmd);
265 	}
266 
267 	mode->mo_close(hdl);
268 }
269