1 /*
2  * waiter_sample.c
3  *
4  * OpenIPMI test code how to use OS handler waiters for blocking code.
5  *
6  * Author: Corey Minyard <minyard@acm.org>
7  *
8  *  This program 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 of
11  *  the License, or (at your option) any later version.
12  *
13  *
14  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
20  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
22  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *  You should have received a copy of the GNU Lesser General Public
26  *  License along with this program; if not, write to the Free
27  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <netdb.h>
38 #include <ctype.h>
39 #include <time.h>
40 
41 #include <OpenIPMI/ipmiif.h>
42 #include <OpenIPMI/ipmi_smi.h>
43 #include <OpenIPMI/ipmi_err.h>
44 #include <OpenIPMI/ipmi_auth.h>
45 #include <OpenIPMI/ipmi_lan.h>
46 #include <OpenIPMI/ipmi_posix.h>
47 
48 /* This sample application demostrates some general handling of sensors,
49    like reading values, setting up events, and things of that nature.
50    It also demonstrates some good coding practices like refcounting
51    structures. */
52 
53 static const char *progname;
54 
55 #define MAX_SENSORS 128
56 
57 struct waiter_data
58 {
59     os_handler_waiter_t *waiter;
60     int err;
61     ipmi_sensor_id_t sensors[MAX_SENSORS];
62     int sensors_type[MAX_SENSORS];
63     unsigned int num_sensors;
64     unsigned int curr;
65 
66     /* values from a threshold sensor. */
67     enum ipmi_value_present_e value_present;
68     unsigned int              raw_value;
69     double                    val;
70 
71     /* values from a discrete and a threshold sensor */
72     ipmi_states_t *states;
73 };
74 
75 void
setup_done(ipmi_domain_t * domain,int err,unsigned int conn_num,unsigned int port_num,int still_connected,void * cb_data)76 setup_done(ipmi_domain_t *domain,
77 	   int           err,
78 	   unsigned int  conn_num,
79 	   unsigned int  port_num,
80 	   int           still_connected,
81 	   void          *cb_data)
82 {
83     struct waiter_data *wd = cb_data;
84 
85     if (err) {
86 	wd->err = err;
87 	os_handler_waiter_release(wd->waiter);
88     }
89 }
90 
91 void
fully_up(ipmi_domain_t * domain,void * cb_data)92 fully_up(ipmi_domain_t *domain, void *cb_data)
93 {
94     struct waiter_data *wd = cb_data;
95 
96     wd->err = 0;
97     os_handler_waiter_release(wd->waiter);
98 }
99 
100 void
sensor_handler(ipmi_entity_t * entity,ipmi_sensor_t * sensor,void * cb_data)101 sensor_handler(ipmi_entity_t *entity, ipmi_sensor_t *sensor, void *cb_data)
102 {
103     struct waiter_data *wd = cb_data;
104 
105     if (wd->num_sensors >= MAX_SENSORS)
106 	return;
107 
108     wd->sensors[wd->num_sensors] = ipmi_sensor_convert_to_id(sensor);
109     wd->sensors_type[wd->num_sensors]
110 	= ipmi_sensor_get_event_reading_type(sensor);
111 
112     wd->num_sensors++;
113 }
114 
115 void
entity_iterate_sensors(ipmi_entity_t * entity,void * cb_data)116 entity_iterate_sensors(ipmi_entity_t *entity, void *cb_data)
117 {
118     ipmi_entity_iterate_sensors(entity, sensor_handler, cb_data);
119 }
120 
121 void
domain_iterate_entities(ipmi_domain_t * domain,void * cb_data)122 domain_iterate_entities(ipmi_domain_t *domain, void *cb_data)
123 {
124     ipmi_domain_iterate_entities(domain, entity_iterate_sensors, cb_data);
125 }
126 
127 void
close_done(void * cb_data)128 close_done(void *cb_data)
129 {
130     struct waiter_data *wd = cb_data;
131 
132     os_handler_waiter_release(wd->waiter);
133 }
134 
135 void
domain_close(ipmi_domain_t * domain,void * cb_data)136 domain_close(ipmi_domain_t *domain, void *cb_data)
137 {
138     struct waiter_data *wd = cb_data;
139 
140     wd->err = ipmi_domain_close(domain, close_done, cb_data);
141     if (wd->err)
142 	os_handler_waiter_release(wd->waiter);
143 }
144 
145 static void
handle_sensor_reading(ipmi_sensor_t * sensor,int err,enum ipmi_value_present_e value_present,unsigned int raw_value,double val,ipmi_states_t * states,void * cb_data)146 handle_sensor_reading(ipmi_sensor_t             *sensor,
147 		      int                       err,
148 		      enum ipmi_value_present_e value_present,
149 		      unsigned int              raw_value,
150 		      double                    val,
151 		      ipmi_states_t             *states,
152 		      void                      *cb_data)
153 {
154     struct waiter_data *wd = cb_data;
155     enum ipmi_thresh_e thresh;
156     char name[IPMI_SENSOR_NAME_LEN];
157 
158     ipmi_sensor_get_name(sensor, name, sizeof(name));
159     if (err) {
160 	printf("Error 0x%x getting discrete states for sensor %s\n",
161 	       err, name);
162 	goto out;
163     }
164 
165     printf("Got threshold reading for sensor %s\n", name);
166     if (ipmi_is_event_messages_enabled(states))
167 	printf("  event messages enabled\n");
168     if (ipmi_is_sensor_scanning_enabled(states))
169 	printf("  sensor scanning enabled\n");
170     if (ipmi_is_initial_update_in_progress(states))
171 	printf("  initial update in progress\n");
172 
173     switch (value_present)
174     {
175     case IPMI_NO_VALUES_PRESENT:
176 	printf("  no value present\n");
177 	break;
178     case IPMI_BOTH_VALUES_PRESENT:
179 	{
180 	    const char *percent = "";
181 	    const char *base;
182 	    const char *mod_use = "";
183 	    const char *modifier = "";
184 	    const char *rate;
185 
186 	    base = ipmi_sensor_get_base_unit_string(sensor);
187 	    if (ipmi_sensor_get_percentage(sensor))
188 		percent = "%";
189 	    switch (ipmi_sensor_get_modifier_unit_use(sensor)) {
190 	    case IPMI_MODIFIER_UNIT_NONE:
191 		break;
192 	    case IPMI_MODIFIER_UNIT_BASE_DIV_MOD:
193 		mod_use = "/";
194 		modifier = ipmi_sensor_get_modifier_unit_string(sensor);
195 		break;
196 	    case IPMI_MODIFIER_UNIT_BASE_MULT_MOD:
197 		mod_use = "*";
198 		modifier = ipmi_sensor_get_modifier_unit_string(sensor);
199 		break;
200 	    }
201 	    rate = ipmi_sensor_get_rate_unit_string(sensor);
202 
203 	    printf("  value: %lf%s %s%s%s%s\n", val, percent,
204 		   base, mod_use, modifier, rate);
205 	}
206 	/* FALLTHROUGH */
207     case IPMI_RAW_VALUE_PRESENT:
208 	printf("  raw value: 0x%2.2x\n", raw_value);
209     }
210 
211     if (ipmi_sensor_get_threshold_access(sensor)
212 	== IPMI_THRESHOLD_ACCESS_SUPPORT_NONE)
213 	goto out;
214 
215     for (thresh=IPMI_LOWER_NON_CRITICAL;
216 	 thresh<=IPMI_UPPER_NON_RECOVERABLE;
217 	 thresh++)
218     {
219 	int val, rv;
220 
221 	rv = ipmi_sensor_threshold_reading_supported(sensor, thresh, &val);
222 	if (rv || !val)
223 	    continue;
224 
225 	if (ipmi_is_threshold_out_of_range(states, thresh))
226 	    printf("  Threshold %s is out of range\n",
227 		   ipmi_get_threshold_string(thresh));
228 	else
229 	    printf("  Threshold %s is in range\n",
230 		   ipmi_get_threshold_string(thresh));
231     }
232 
233  out:
234     os_handler_waiter_release(wd->waiter);
235 }
236 
237 static void
handle_sensor_states(ipmi_sensor_t * sensor,int err,ipmi_states_t * states,void * cb_data)238 handle_sensor_states(ipmi_sensor_t *sensor,
239 		     int           err,
240 		     ipmi_states_t *states,
241 		     void          *cb_data)
242 {
243     struct waiter_data *wd = cb_data;
244     int  i;
245     char name[IPMI_SENSOR_NAME_LEN];
246 
247     ipmi_sensor_get_name(sensor, name, sizeof(name));
248     if (err) {
249 	printf("Error 0x%x getting discrete states for sensor %s\n",
250 	       err, name);
251 	goto out;
252     }
253 
254     printf("Got state reading for sensor %s\n", name);
255     if (ipmi_is_event_messages_enabled(states))
256 	printf("  event messages enabled\n");
257     if (ipmi_is_sensor_scanning_enabled(states))
258 	printf("  sensor scanning enabled\n");
259     if (ipmi_is_initial_update_in_progress(states))
260 	printf("  initial update in progress\n");
261 
262     for (i=0; i<15; i++) {
263 	int val, rv;
264 
265 	rv = ipmi_sensor_discrete_event_readable(sensor, i, &val);
266 	if (rv || !val)
267 	    continue;
268 
269 	printf("  state %d value is %d\n", i, ipmi_is_state_set(states, i));
270     }
271 
272  out:
273     os_handler_waiter_release(wd->waiter);
274 }
275 
276 int
main(int argc,char * argv[])277 main(int argc, char *argv[])
278 {
279     int         rv;
280     int         curr_arg = 1;
281     ipmi_args_t *args;
282     ipmi_con_t  *con;
283     os_handler_waiter_factory_t *waiterf;
284     os_handler_t *os_hnd;
285     char	ebuf[128];
286     ipmi_domain_id_t domain_id;
287 
288 
289     /*
290      * We can do this without dynamic allocation because this function will
291      * never be exited until the progran is done.
292      */
293     struct waiter_data waiter_space;
294     struct waiter_data *wd = &waiter_space;
295 
296     progname = argv[0];
297 
298     /* OS handler allocated first. */
299     os_hnd = ipmi_posix_setup_os_handler();
300     if (!os_hnd) {
301 	printf("ipmi_smi_setup_con: Unable to allocate os handler\n");
302 	exit(1);
303     }
304 
305     /* Use the default log handler. */
306 
307     /* Initialize the OpenIPMI library. */
308     ipmi_init(os_hnd);
309 
310     rv = ipmi_parse_args2(&curr_arg, argc, argv, &args);
311     if (rv) {
312 	fprintf(stderr, "Error parsing command arguments, argument %d: %s\n",
313 		curr_arg, ipmi_get_error_string(rv, ebuf, sizeof(ebuf)));
314 	exit(1);
315     }
316 
317     rv = ipmi_args_setup_con(args, os_hnd, NULL, &con);
318     if (rv) {
319         fprintf(stderr, "ipmi_ip_setup_con: %s",
320 		ipmi_get_error_string(rv, ebuf, sizeof(ebuf)));
321 	exit(1);
322     }
323 
324     rv = os_handler_alloc_waiter_factory(os_hnd, 0, 0, &waiterf);
325     if (rv) {
326         fprintf(stderr, "os_handler_alloc_waiter_factory: %s",
327 		ipmi_get_error_string(rv, ebuf, sizeof(ebuf)));
328 	exit(1);
329     }
330 
331     wd->num_sensors = 0;
332     wd->waiter = os_handler_alloc_waiter(waiterf);
333     if (!wd->waiter) {
334         fprintf(stderr, "os_handler_alloc_waiter: Out of memory");
335 	exit(1);
336     }
337 
338     rv = ipmi_open_domain("", &con, 1, setup_done, wd, fully_up, wd,
339 			  NULL, 0, &domain_id);
340     if (rv) {
341 	fprintf(stderr, "ipmi_init_domain: %s\n",
342 		ipmi_get_error_string(rv, ebuf, sizeof(ebuf)));
343 	exit(1);
344     }
345 
346     os_handler_waiter_wait(wd->waiter, NULL);
347     if (wd->err) {
348 	fprintf(stderr, "Error starting connection: %s\n",
349 		ipmi_get_error_string(wd->err, ebuf, sizeof(ebuf)));
350     }
351 
352     /*
353      * At this point the domain is fully up.  We can iterate the
354      * sensors now.  First get a list of all sensor ids.
355      */
356     ipmi_domain_pointer_cb(domain_id, domain_iterate_entities, wd);
357 
358     /*
359      * Now scan the sensors
360      */
361     for (wd->curr = 0; wd->curr < wd->num_sensors; wd->curr++) {
362 	os_handler_waiter_use(wd->waiter);
363 	if (wd->sensors_type[wd->curr] == IPMI_EVENT_READING_TYPE_THRESHOLD)
364 	    rv = ipmi_sensor_id_get_reading(wd->sensors[wd->curr],
365 					    handle_sensor_reading, wd);
366 	else
367 	    rv = ipmi_sensor_id_get_states(wd->sensors[wd->curr],
368 					   handle_sensor_states, wd);
369 	if (rv) {
370 	    fprintf(stderr, "Error reading sensor: %s\n",
371 		    ipmi_get_error_string(rv, ebuf, sizeof(ebuf)));
372 	    continue;
373 	}
374 	os_handler_waiter_wait(wd->waiter, NULL);
375     }
376 
377     wd->err = 0;
378     os_handler_waiter_use(wd->waiter);
379     rv = ipmi_domain_pointer_cb(domain_id, domain_close, wd);
380     if (rv) {
381 	fprintf(stderr, "close ptr cb: %s\n",
382 		ipmi_get_error_string(rv, ebuf, sizeof(ebuf)));
383 	exit(1);
384     }
385     os_handler_waiter_wait(wd->waiter, NULL);
386     if (wd->err) {
387 	fprintf(stderr, "ipmi_domain_close: %s\n",
388 		ipmi_get_error_string(wd->err, ebuf, sizeof(ebuf)));
389     }
390 
391     /* Technically, we can't get here, but this is an example. */
392     os_hnd->free_os_handler(os_hnd);
393     return 0;
394 }
395