1 /*****************************************************************************\
2  *  $Id: ipmi-dcmi.c,v 1.15 2010-07-27 18:01:43 chu11 Exp $
3  *****************************************************************************
4  *  Copyright (C) 2009-2015 Lawrence Livermore National Security, LLC.
5  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
6  *  Written by Albert Chu <chu11@llnl.gov>
7  *  LLNL-CODE-413270
8  *
9  *  This file is part of Ipmi-Dcmi, tools and libraries to support the
10  *  data center manageability interface (DCMI).  For details, see
11  *  http://www.llnl.gov/linux/.
12  *
13  *  Ipmi-Dcmi is free software; you can redistribute it and/or modify
14  *  it under the terms of the GNU General Public License as published by the
15  *  Free Software Foundation; either version 3 of the License, or (at your
16  *  option) any later version.
17  *
18  *  Ipmi-Dcmi is distributed in the hope that it will be useful, but
19  *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20  *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21  *  for more details.
22  *
23  *  You should have received a copy of the GNU General Public License along
24  *  with Ipmi-Dcmi.  If not, see <http://www.gnu.org/licenses/>.
25 \*****************************************************************************/
26 
27 #if HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #if STDC_HEADERS
34 #include <string.h>
35 #endif /* STDC_HEADERS */
36 #if TIME_WITH_SYS_TIME
37 #include <sys/time.h>
38 #include <time.h>
39 #else /* !TIME_WITH_SYS_TIME */
40 #if HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #else /* !HAVE_SYS_TIME_H */
43 #include <time.h>
44 #endif /* !HAVE_SYS_TIME_H */
45 #endif /* !TIME_WITH_SYS_TIME */
46 #include <assert.h>
47 #include <errno.h>
48 
49 #include <freeipmi/freeipmi.h>
50 
51 #include "ipmi-dcmi.h"
52 #include "ipmi-dcmi-argp.h"
53 
54 #include "freeipmi-portability.h"
55 #include "pstdout.h"
56 #include "tool-common.h"
57 #include "tool-cmdline-common.h"
58 #include "tool-hostrange-common.h"
59 #include "tool-util-common.h"
60 
61 #define IPMI_DCMI_ROLLING_AVERAGE_TIME_PERIOD_BUFLEN 4096
62 
63 #define IPMI_DCMI_MAX_RECORD_IDS_BUFLEN 1024
64 
65 #define IPMI_DCMI_ERROR_BUFLEN          1024
66 
67 #define IPMI_DCMI_TIME_BUFLEN           512
68 
69 /* return 1 on output success, 0 on no output, -1 on error */
70 static int
71 _dcmi_specification_conformance (ipmi_dcmi_state_data_t *state_data, uint8_t *parameter_revision)
72 {
73   fiid_obj_t obj_cmd_rs = NULL;
74   uint8_t major, minor;
75   uint64_t val;
76   int rv = -1;
77 
78   assert (state_data);
79   assert (parameter_revision);
80 
81   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_capability_info_supported_dcmi_capabilities_rs)))
82     {
83       pstdout_fprintf (state_data->pstate,
84                        stderr,
85                        "fiid_obj_create: %s\n",
86                        strerror (errno));
87       goto cleanup;
88     }
89 
90   if (ipmi_cmd_dcmi_get_dcmi_capability_info_supported_dcmi_capabilities (state_data->ipmi_ctx,
91                                                                           obj_cmd_rs) < 0)
92     {
93       pstdout_fprintf (state_data->pstate,
94                        stderr,
95                        "ipmi_cmd_dcmi_get_dcmi_capability_info_supported_dcmi_capabilities: %s\n",
96                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
97       goto cleanup;
98     }
99 
100   if (FIID_OBJ_GET (obj_cmd_rs,
101                     "dcmi_specification_conformance.major_version",
102                     &val) < 0)
103     {
104       pstdout_fprintf (state_data->pstate,
105                        stderr,
106                        "fiid_obj_get: 'dcmi_specification_conformance.major_version': %s\n",
107                        fiid_obj_errormsg (obj_cmd_rs));
108       goto cleanup;
109     }
110   major = val;
111 
112   if (FIID_OBJ_GET (obj_cmd_rs,
113                     "dcmi_specification_conformance.minor_version",
114                     &val) < 0)
115     {
116       pstdout_fprintf (state_data->pstate,
117                        stderr,
118                        "fiid_obj_get: 'dcmi_specification_conformance.minor_version': %s\n",
119                        fiid_obj_errormsg (obj_cmd_rs));
120       goto cleanup;
121     }
122   minor = val;
123 
124   /* XXX: achu: The spec does not say how these version numbers are
125    * formmatted.  decimal?  BCD?  On the one hand, I think to be
cmdline_parse(int key,char * arg,struct argp_state * state)126    * consistent to the "IPMI Version" of a Get Device ID call, it
127    * should be BCD.  But, these are 8 bit fields instead of 4 bit
128    * fields (e.g. would I output "01.00" instead of "1.0"?).  So I'm
129    * going to assume decimal for now.
130    */
131   pstdout_printf (state_data->pstate,
132                   "DCMI Specification Conformance                     : %u.%u\n",
133                   major,
134                   minor);
135 
136   if (FIID_OBJ_GET (obj_cmd_rs,
137                     "parameter_revision",
138                     &val) < 0)
139     {
140       pstdout_fprintf (state_data->pstate,
141                        stderr,
142                        "fiid_obj_get: 'parameter_revision': %s\n",
143                        fiid_obj_errormsg (obj_cmd_rs));
144       goto cleanup;
145     }
146   (*parameter_revision) = val;
147 
148   rv = 1;
149  cleanup:
150   fiid_obj_destroy (obj_cmd_rs);
151   return (rv);
152 }
153 
154 /* return 1 on output success, 0 on no output, -1 on error */
155 static int
156 _supported_dcmi_capabilities (ipmi_dcmi_state_data_t *state_data)
157 {
158   fiid_obj_t obj_cmd_rs = NULL;
159   uint8_t parameter_revision;
160   uint64_t val;
161   int rv = -1;
162 
163   assert (state_data);
164 
165   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_capability_info_supported_dcmi_capabilities_rs)))
166     {
167       pstdout_fprintf (state_data->pstate,
168                        stderr,
169                        "fiid_obj_create: %s\n",
170                        strerror (errno));
171       goto cleanup;
172     }
173 
174   if (ipmi_cmd_dcmi_get_dcmi_capability_info_supported_dcmi_capabilities (state_data->ipmi_ctx,
175                                                                           obj_cmd_rs) < 0)
176     {
177       pstdout_fprintf (state_data->pstate,
178                        stderr,
179                        "ipmi_cmd_dcmi_get_dcmi_capability_info_supported_dcmi_capabilities: %s\n",
180                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
181       goto cleanup;
182     }
183 
184   if (FIID_OBJ_GET (obj_cmd_rs,
185                     "parameter_revision",
186                     &val) < 0)
187     {
188       pstdout_fprintf (state_data->pstate,
189                        stderr,
190                        "fiid_obj_get: 'mandatory_platform_capabilities.identification_support': %s\n",
191                        fiid_obj_errormsg (obj_cmd_rs));
192       goto cleanup;
193     }
194   parameter_revision = val;
195 
196   /* See errata 1.0 */
197   if (!(parameter_revision >= 0x02))
198     {
199       if (FIID_OBJ_GET (obj_cmd_rs,
200                         "mandatory_platform_capabilities.identification_support",
201                         &val) < 0)
202         {
203           pstdout_fprintf (state_data->pstate,
204                            stderr,
205                            "fiid_obj_get: 'mandatory_platform_capabilities.identification_support': %s\n",
206                            fiid_obj_errormsg (obj_cmd_rs));
207           goto cleanup;
208         }
209 
210       pstdout_printf (state_data->pstate,
211                       "Identification Support                             : %s\n",
212                       val ? "Compliant with DCMI Specification" : "Not Compliant with DCMI Specification");
213 
214       if (FIID_OBJ_GET (obj_cmd_rs,
215                         "mandatory_platform_capabilities.sel_logging",
216                         &val) < 0)
217         {
218           pstdout_fprintf (state_data->pstate,
219                            stderr,
220                            "fiid_obj_get: 'mandatory_platform_capabilities.sel_logging': %s\n",
221                            fiid_obj_errormsg (obj_cmd_rs));
222           goto cleanup;
223         }
224 
225       pstdout_printf (state_data->pstate,
226                       "SEL logging                                        : %s\n",
227                       val ? "Compliant with DCMI Specification" : "Not Compliant with DCMI Specification");
228 
229       if (FIID_OBJ_GET (obj_cmd_rs,
230                         "mandatory_platform_capabilities.chassis_power",
231                         &val) < 0)
232         {
233           pstdout_fprintf (state_data->pstate,
234                            stderr,
235                            "fiid_obj_get: 'mandatory_platform_capabilities.chassis_power': %s\n",
236                            fiid_obj_errormsg (obj_cmd_rs));
237           goto cleanup;
238         }
239 
240       pstdout_printf (state_data->pstate,
241                       "Chassis Power                                      : %s\n",
242                       val ? "Compliant with DCMI Specification" : "Not Compliant with DCMI Specification");
243 
244       if (FIID_OBJ_GET (obj_cmd_rs,
245                         "mandatory_platform_capabilities.temperature_monitor",
246                         &val) < 0)
247         {
248           pstdout_fprintf (state_data->pstate,
249                            stderr,
250                            "fiid_obj_get: 'mandatory_platform_capabilities.temperature_monitor': %s\n",
251                            fiid_obj_errormsg (obj_cmd_rs));
252           goto cleanup;
253         }
254 
255       pstdout_printf (state_data->pstate,
256                       "Temperature Monitor                                : %s\n",
257                       val ? "Compliant with DCMI Specification" : "Not Compliant with DCMI Specification");
258     }
259 
260   if (FIID_OBJ_GET (obj_cmd_rs,
261                     "optional_platform_capabilities.power_management_monitoring_support",
262                     &val) < 0)
263     {
264       pstdout_fprintf (state_data->pstate,
265                        stderr,
266                        "fiid_obj_get: 'optional_platform_capabilities.power_management_monitoring_support': %s\n",
267                        fiid_obj_errormsg (obj_cmd_rs));
268       goto cleanup;
269     }
270 
271   pstdout_printf (state_data->pstate,
272                   "Power Management / Monitoring Support              : %s\n",
273                   val ? "Available" : "Not present");
274 
275   if (FIID_OBJ_GET (obj_cmd_rs,
276                     "manageability_access_capabilities.in_band_system_interface_channel_available",
277                     &val) < 0)
278     {
279       pstdout_fprintf (state_data->pstate,
280                        stderr,
281                        "fiid_obj_get: 'manageability_access_capabilities.in_band_system_interface_channel_available': %s\n",
282                        fiid_obj_errormsg (obj_cmd_rs));
283       goto cleanup;
284     }
285 
286   pstdout_printf (state_data->pstate,
287                   "In-band System Interface Channel                   : %s\n",
288                   val ? "Available" : "Not present");
289 
290   if (FIID_OBJ_GET (obj_cmd_rs,
291                     "manageability_access_capabilities.serial_tmode_available",
292                     &val) < 0)
293     {
294       pstdout_fprintf (state_data->pstate,
295                        stderr,
296                        "fiid_obj_get: 'manageability_access_capabilities.serial_tmode_available': %s\n",
297                        fiid_obj_errormsg (obj_cmd_rs));
298       goto cleanup;
299     }
300 
301   pstdout_printf (state_data->pstate,
302                   "Serial TMODE                                       : %s\n",
303                   val ? "Available" : "Not present");
304 
305   if (FIID_OBJ_GET (obj_cmd_rs,
306                     "manageability_access_capabilities.out_of_band_secondary_lan_channel_available",
307                     &val) < 0)
308     {
_ipmi_dcmi_config_file_parse(struct ipmi_dcmi_arguments * cmd_args)309       pstdout_fprintf (state_data->pstate,
310                        stderr,
311                        "fiid_obj_get: 'manageability_access_capabilities.out_of_band_secondary_lan_channel_available': %s\n",
312                        fiid_obj_errormsg (obj_cmd_rs));
313       goto cleanup;
314     }
315 
316   pstdout_printf (state_data->pstate,
317                   "Out-Of-Band Secondary LAN Channel                  : %s\n",
318                   val ? "Available" : "Not present");
319 
320   /* See errata 1.0 */
321   if (!(parameter_revision >= 0x02))
322     {
323       if (FIID_OBJ_GET (obj_cmd_rs,
324                         "manageability_access_capabilities.out_of_band_primary_lan_channel_available",
325                         &val) < 0)
326         {
327           pstdout_fprintf (state_data->pstate,
328                            stderr,
329                            "fiid_obj_get: 'manageability_access_capabilities.out_of_band_primary_lan_channel_available': %s\n",
330                            fiid_obj_errormsg (obj_cmd_rs));
331           goto cleanup;
332         }
333 
334       pstdout_printf (state_data->pstate,
_ipmi_dcmi_args_validate(struct ipmi_dcmi_arguments * cmd_args)335                       "Out-Of-Band Primary LAN Channel                    : %s\n",
336                       val ? "Available" : "Not present");
337 
338       if (FIID_OBJ_GET (obj_cmd_rs,
339                         "manageability_access_capabilities.sol_supported",
340                         &val) < 0)
341         {
342           pstdout_fprintf (state_data->pstate,
343                            stderr,
344                            "fiid_obj_get: 'manageability_access_capabilities.sol_supported': %s\n",
345                            fiid_obj_errormsg (obj_cmd_rs));
346           goto cleanup;
347         }
348 
349       /* SOL Supported - removed "supported" */
350       pstdout_printf (state_data->pstate,
351                       "SOL                                                : %s\n",
352                       val ? "Available" : "Not present");
353 
354       if (FIID_OBJ_GET (obj_cmd_rs,
355                         "manageability_access_capabilities.vlan_capable",
356                         &val) < 0)
357         {
358           pstdout_fprintf (state_data->pstate,
359                            stderr,
360                            "fiid_obj_get: 'manageability_access_capabilities.vlan_capable': %s\n",
361                            fiid_obj_errormsg (obj_cmd_rs));
362           goto cleanup;
363         }
364 
365       /* VLAN Capable - removed "capable" */
366       pstdout_printf (state_data->pstate,
367                       "VLAN                                               : %s\n",
368                       val ? "Available" : "Not present");
369     }
370 
371   rv = 1;
372  cleanup:
373   fiid_obj_destroy (obj_cmd_rs);
374   return (rv);
375 }
376 
377 /* return 1 on output success, 0 on no output, -1 on error */
378 static int
379 _mandatory_platform_attributes (ipmi_dcmi_state_data_t *state_data)
380 {
381   fiid_obj_t obj_cmd_rs = NULL;
382   uint8_t parameter_revision;
383   uint16_t number_of_sel_entries;
384   uint64_t val;
385   int rv = -1;
ipmi_dcmi_argp_parse(int argc,char ** argv,struct ipmi_dcmi_arguments * cmd_args)386 
387   assert (state_data);
388 
389   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_capability_info_mandatory_platform_attributes_rs)))
390     {
391       pstdout_fprintf (state_data->pstate,
392                        stderr,
393                        "fiid_obj_create: %s\n",
394                        strerror (errno));
395       goto cleanup;
396     }
397 
398   if (ipmi_cmd_dcmi_get_dcmi_capability_info_mandatory_platform_attributes (state_data->ipmi_ctx,
399                                                                             obj_cmd_rs) < 0)
400     {
401       pstdout_fprintf (state_data->pstate,
402                        stderr,
403                        "ipmi_cmd_dcmi_get_dcmi_capability_info_mandatory_platform_attributes: %s\n",
404                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
405       goto cleanup;
406     }
407 
408   if (FIID_OBJ_GET (obj_cmd_rs,
409                     "parameter_revision",
410                     &val) < 0)
411     {
412       pstdout_fprintf (state_data->pstate,
413                        stderr,
414                        "fiid_obj_get: 'mandatory_platform_capabilities.identification_support': %s\n",
415                        fiid_obj_errormsg (obj_cmd_rs));
416       goto cleanup;
417     }
418   parameter_revision = val;
419 
420   if (FIID_OBJ_GET (obj_cmd_rs,
421                     "sel_attributes.number_of_sel_entries",
422                     &val) < 0)
423     {
424       pstdout_fprintf (state_data->pstate,
425                        stderr,
426                        "fiid_obj_get: 'sel_attributes.number_of_sel_entries': %s\n",
427                        fiid_obj_errormsg (obj_cmd_rs));
428       goto cleanup;
429     }
430   number_of_sel_entries = val;
431 
432   pstdout_printf (state_data->pstate,
433                   "Number of SEL entries                              : %u\n",
434                   number_of_sel_entries);
435 
436   /* In DCMI v1.1 */
437   if (parameter_revision >= 0x02)
438     {
439       if (FIID_OBJ_GET (obj_cmd_rs,
440                         "sel_attributes.record_level_sel_flush_upon_rollover",
441                         &val) < 0)
442         {
443           pstdout_fprintf (state_data->pstate,
444                            stderr,
445                            "fiid_obj_get: 'sel_attributes.record_level_sel_flush_upon_rollover': %s\n",
446                            fiid_obj_errormsg (obj_cmd_rs));
447           goto cleanup;
448         }
449 
450       pstdout_printf (state_data->pstate,
451                       "Record Level SEL Flush upon Rollover               : %s\n",
452                       val ? "Available" : "Not present");
453 
454       if (FIID_OBJ_GET (obj_cmd_rs,
455                         "sel_attributes.entire_sel_flush_upon_rollover",
456                         &val) < 0)
457         {
458           pstdout_fprintf (state_data->pstate,
459                            stderr,
460                            "fiid_obj_get: 'sel_attributes.entire_sel_flush_upon_rollover': %s\n",
461                            fiid_obj_errormsg (obj_cmd_rs));
462           goto cleanup;
463         }
464 
465       pstdout_printf (state_data->pstate,
466                       "Entire SEL Flush upon Rollover                     : %s\n",
467                       val ? "Available" : "Not present");
468     }
469 
470   if (FIID_OBJ_GET (obj_cmd_rs,
471                     "sel_attributes.sel_automatic_rollover_enabled",
472                     &val) < 0)
473     {
474       pstdout_fprintf (state_data->pstate,
475                        stderr,
476                        "fiid_obj_get: 'sel_attributes.sel_automatic_rollover_enabled': %s\n",
477                        fiid_obj_errormsg (obj_cmd_rs));
478       goto cleanup;
479     }
480 
481   pstdout_printf (state_data->pstate,
482                   "SEL automatic rollover                             : %s\n",
483                   val ? "Available" : "Not present");
484 
485   /* See errata 1.0 */
486   if (!(parameter_revision >= 0x02))
487     {
488       if (FIID_OBJ_GET (obj_cmd_rs,
489                         "identification_attributes.guid_support",
490                         &val) < 0)
491         {
492           pstdout_fprintf (state_data->pstate,
493                            stderr,
494                            "fiid_obj_get: 'identification_attributes.guid_support': %s\n",
495                            fiid_obj_errormsg (obj_cmd_rs));
496           goto cleanup;
497         }
498 
499       pstdout_printf (state_data->pstate,
500                       "GUID                                               : %s\n",
501                       val ? "Available" : "Not present");
502     }
503 
504   if (FIID_OBJ_GET (obj_cmd_rs,
505                     "identification_attributes.dhcp_host_name_support",
506                     &val) < 0)
507     {
508       pstdout_fprintf (state_data->pstate,
509                        stderr,
510                        "fiid_obj_get: 'identification_attributes.dhcp_host_name_support': %s\n",
511                        fiid_obj_errormsg (obj_cmd_rs));
512       goto cleanup;
513     }
514 
515   pstdout_printf (state_data->pstate,
516                   "DHCP Host Name                                     : %s\n",
517                   val ? "Available" : "Not present");
518 
519   if (FIID_OBJ_GET (obj_cmd_rs,
520                     "identification_attributes.asset_tag_support",
521                     &val) < 0)
522     {
523       pstdout_fprintf (state_data->pstate,
524                        stderr,
525                        "fiid_obj_get: 'identification_attributes.asset_tag_support': %s\n",
526                        fiid_obj_errormsg (obj_cmd_rs));
527       goto cleanup;
528     }
529 
530   pstdout_printf (state_data->pstate,
531                   "Asset Tag                                          : %s\n",
532                   val ? "Available" : "Not present");
533 
534   /* See errata 1.0 */
535   if (!(parameter_revision >= 0x02))
536     {
537       if (FIID_OBJ_GET (obj_cmd_rs,
538                     "temperature_monitoring.inlet_temperature",
539                         &val) < 0)
540         {
541           pstdout_fprintf (state_data->pstate,
542                            stderr,
543                            "fiid_obj_get: 'temperature_monitoring.inlet_temperature': %s\n",
544                            fiid_obj_errormsg (obj_cmd_rs));
545           goto cleanup;
546         }
547 
548       pstdout_printf (state_data->pstate,
549                       "Inlet temperature                                  : %s\n",
550                       val ? "At least 1 present" : "Not present");
551 
552       if (FIID_OBJ_GET (obj_cmd_rs,
553                         "temperature_monitoring.processors_temperature",
554                         &val) < 0)
555         {
556           pstdout_fprintf (state_data->pstate,
557                            stderr,
558                            "fiid_obj_get: 'temperature_monitoring.processors_temperature': %s\n",
559                            fiid_obj_errormsg (obj_cmd_rs));
560           goto cleanup;
561         }
562 
563       pstdout_printf (state_data->pstate,
564                       "Processors temperature                             : %s\n",
565                       val ? "At least 1 present" : "Not present");
566 
567       if (FIID_OBJ_GET (obj_cmd_rs,
568                         "temperature_monitoring.baseboard_temperature",
569                         &val) < 0)
570         {
571           pstdout_fprintf (state_data->pstate,
572                            stderr,
573                            "fiid_obj_get: 'temperature_monitoring.baseboard_temperature': %s\n",
574                            fiid_obj_errormsg (obj_cmd_rs));
575           goto cleanup;
576         }
577 
578       pstdout_printf (state_data->pstate,
579                       "Baseboard temperature                              : %s\n",
580                       val ? "At least 1 present" : "Not present");
581     }
582 
583   /* In DCMI v1.1 */
584   if (parameter_revision >= 0x02)
585     {
586       int flag;
587 
588       if ((flag = fiid_obj_get (obj_cmd_rs,
589                                 "temperature_monitoring.sampling_period",
590                                 &val)) < 0)
591         {
592           pstdout_fprintf (state_data->pstate,
593                            stderr,
594                            "fiid_obj_get: 'temperature_monitoring.sampling_period': %s\n",
595                            fiid_obj_errormsg (obj_cmd_rs));
596           goto cleanup;
597         }
598 
599       if (flag)
600         {
601           pstdout_printf (state_data->pstate,
602                       "Sampling frequency for Temperature Monitoring      : Every %u Second(s)\n",
603                       val);
604         }
605     }
606 
607   rv = 1;
608  cleanup:
609   fiid_obj_destroy (obj_cmd_rs);
610   return (rv);
611 }
612 
613 /* return 1 on output success, 0 on no output, -1 on error */
614 static int
615 _optional_platform_attributes (ipmi_dcmi_state_data_t *state_data)
616 {
617   fiid_obj_t obj_cmd_rs = NULL;
618   uint8_t slave_address;
619   uint8_t device_revision;
620   uint8_t channel_number;
621   uint64_t val;
622   int rv = -1;
623 
624   assert (state_data);
625 
626   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_capability_info_optional_platform_attributes_rs)))
627     {
628       pstdout_fprintf (state_data->pstate,
629                        stderr,
630                        "fiid_obj_create: %s\n",
631                        strerror (errno));
632       goto cleanup;
633     }
634 
635   if (ipmi_cmd_dcmi_get_dcmi_capability_info_optional_platform_attributes (state_data->ipmi_ctx,
636                                                                            obj_cmd_rs) < 0)
637     {
638       /* IPMI Workaround?
639        *
640        * Technically, I believe this should be mandatory, as this is
641        * not listed as optional in the DCMI spec (enhanced system
642        * power statistics attributes is the only one that is
643        * optional).  However, this parameter specifically lists
644        * attributes for optional parts of DCMI.
645        *
646        * This has been seen as non-implemented on atleast two
647        * motherboards (one undocumented motherboard and also the Intel
648        * S2600JF/Appro 512X).
649        */
650       if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
651           && ((ipmi_check_completion_code (obj_cmd_rs,
652                                            IPMI_COMP_CODE_REQUEST_PARAMETER_NOT_SUPPORTED) == 1)
653               || (ipmi_check_completion_code (obj_cmd_rs,
654                                               IPMI_COMP_CODE_REQUESTED_SENSOR_DATA_OR_RECORD_NOT_PRESENT) == 1)))
655         {
656           rv = 0;
657           goto cleanup;
658         }
659 
660       pstdout_fprintf (state_data->pstate,
661                        stderr,
662                        "ipmi_cmd_dcmi_get_dcmi_capability_info_optional_platform_attributes: %s\n",
663                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
664       goto cleanup;
665     }
666 
667   if (FIID_OBJ_GET (obj_cmd_rs,
668                     "power_management_device_slave_address.slave_address",
669                     &val) < 0)
670     {
671       pstdout_fprintf (state_data->pstate,
672                        stderr,
673                        "fiid_obj_get: 'power_management_device_slave_address.slave_address': %s\n",
674                        fiid_obj_errormsg (obj_cmd_rs));
675       goto cleanup;
676     }
677   slave_address = val;
678 
679   pstdout_printf (state_data->pstate,
680                   "Power Management Device Slave Address              : %02Xh\n",
681                   slave_address);
682 
683   if (FIID_OBJ_GET (obj_cmd_rs,
684                     "power_management_controller_channel_number.device_revision",
685                     &val) < 0)
686     {
687       pstdout_fprintf (state_data->pstate,
688                        stderr,
689                        "fiid_obj_get: 'power_management_controller_channel_number.device_revision': %s\n",
690                        fiid_obj_errormsg (obj_cmd_rs));
691       goto cleanup;
692     }
693   device_revision = val;
694 
695   /* revision is decimal?  Lets assume so */
696   pstdout_printf (state_data->pstate,
697                   "Power Management Controller Device Revision        : %u\n",
698                   device_revision);
699 
700   if (FIID_OBJ_GET (obj_cmd_rs,
701                     "power_management_controller_channel_number.channel_number",
702                     &val) < 0)
703     {
704       pstdout_fprintf (state_data->pstate,
705                        stderr,
706                        "fiid_obj_get: 'power_management_controller_channel_number.channel_number': %s\n",
707                        fiid_obj_errormsg (obj_cmd_rs));
708       goto cleanup;
709     }
710   channel_number = val;
711 
712   pstdout_printf (state_data->pstate,
713                   "Power Management Controller Channel Number         : %u\n",
714                   channel_number);
715 
716   rv = 1;
717  cleanup:
718   fiid_obj_destroy (obj_cmd_rs);
719   return (rv);
720 }
721 
722 /* return 1 on output success, 0 on no output, -1 on error */
723 static int
724 _manageability_access_attributes (ipmi_dcmi_state_data_t *state_data)
725 {
726   fiid_obj_t obj_cmd_rs = NULL;
727   uint8_t channel_number;
728   uint64_t val;
729   int rv = -1;
730 
731   assert (state_data);
732 
733   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_capability_info_manageability_access_attributes_rs)))
734     {
735       pstdout_fprintf (state_data->pstate,
736                        stderr,
737                        "fiid_obj_create: %s\n",
738                        strerror (errno));
739       goto cleanup;
740     }
741 
742   if (ipmi_cmd_dcmi_get_dcmi_capability_info_manageability_access_attributes (state_data->ipmi_ctx,
743                                                                               obj_cmd_rs) < 0)
744     {
745       pstdout_fprintf (state_data->pstate,
746                        stderr,
747                        "ipmi_cmd_dcmi_get_dcmi_capability_info_manageability_access_attributes: %s\n",
748                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
749       goto cleanup;
750     }
751 
752   if (FIID_OBJ_GET (obj_cmd_rs,
753                     "mandatory_primary_lan_out_of_band_support_channel_number",
754                     &val) < 0)
755     {
756       pstdout_fprintf (state_data->pstate,
757                        stderr,
758                        "fiid_obj_get: 'mandatory_primary_lan_out_of_band_support_channel_number': %s\n",
759                        fiid_obj_errormsg (obj_cmd_rs));
760       goto cleanup;
761     }
762   channel_number = val;
763 
764   if (channel_number == IPMI_DCMI_CHANNEL_NOT_SUPPORTED)
765     pstdout_printf (state_data->pstate,
766                     "Primary LAN Out-of-band Channel Number             : Not supported\n");
767   else
768     pstdout_printf (state_data->pstate,
769                     "Primary LAN Out-of-band Channel Number             : %u\n",
770                     channel_number);
771 
772   if (FIID_OBJ_GET (obj_cmd_rs,
773                     "optional_secondary_lan_out_of_band_support_channel_number",
774                     &val) < 0)
775     {
776       pstdout_fprintf (state_data->pstate,
777                        stderr,
778                        "fiid_obj_get: 'optional_secondary_lan_out_of_band_support_channel_number': %s\n",
779                        fiid_obj_errormsg (obj_cmd_rs));
780       goto cleanup;
781     }
782   channel_number = val;
783 
784   if (channel_number == IPMI_DCMI_CHANNEL_NOT_SUPPORTED)
785     pstdout_printf (state_data->pstate,
786                     "Secondary LAN Out-of-band Channel Number           : Not supported\n");
787   else
788     pstdout_printf (state_data->pstate,
789                     "Secondary LAN Out-of-band Channel Number           : %u\n",
790                     channel_number);
791 
792   if (FIID_OBJ_GET (obj_cmd_rs,
793                     "optional_serial_out_of_band_tmode_capability_channel_number",
794                     &val) < 0)
795     {
796       pstdout_fprintf (state_data->pstate,
797                        stderr,
798                        "fiid_obj_get: 'optional_serial_out_of_band_tmode_capability_channel_number': %s\n",
799                        fiid_obj_errormsg (obj_cmd_rs));
800       goto cleanup;
801     }
802   channel_number = val;
803 
804   if (channel_number == IPMI_DCMI_CHANNEL_NOT_SUPPORTED)
805     pstdout_printf (state_data->pstate,
806                     "Serial Out-of-band TMODE Capability Channel Number : Not supported\n");
807   else
808     pstdout_printf (state_data->pstate,
809                     "Serial Out-of-band TMODE Capability Channel Number : %u\n",
810                     channel_number);
811 
812   rv = 1;
813  cleanup:
814   fiid_obj_destroy (obj_cmd_rs);
815   return (rv);
816 }
817 
818 /* return 1 on output success, 0 on no output, -1 on error */
819 static int
820 _get_enhanced_system_power_statistics_attributes (ipmi_dcmi_state_data_t *state_data,
821                                                   uint8_t *number_of_supported_rolling_average_time_periods,
822                                                   uint8_t *rolling_average_time_periods,
823                                                   unsigned int rolling_average_time_periods_buflen)
824 {
825   fiid_obj_t obj_cmd_rs = NULL;
826   uint64_t val;
827   int len;
828   int rv = -1;
829 
830   assert (state_data);
831   assert (number_of_supported_rolling_average_time_periods);
832   assert (rolling_average_time_periods);
833   assert (rolling_average_time_periods_buflen);
834 
835   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_capability_info_enhanced_system_power_statistics_attributes_rs)))
836     {
837       pstdout_fprintf (state_data->pstate,
838                        stderr,
839                        "fiid_obj_create: %s\n",
840                        strerror (errno));
841       goto cleanup;
842     }
843 
844   if (ipmi_cmd_dcmi_get_dcmi_capability_info_enhanced_system_power_statistics_attributes (state_data->ipmi_ctx,
845                                                                                           obj_cmd_rs) < 0)
846     {
847       /* this optional parameter is not supported */
848       if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
849           && ((ipmi_check_completion_code (obj_cmd_rs,
850                                            IPMI_COMP_CODE_REQUEST_PARAMETER_NOT_SUPPORTED) == 1)
851               || (ipmi_check_completion_code (obj_cmd_rs,
852                                               IPMI_COMP_CODE_REQUESTED_SENSOR_DATA_OR_RECORD_NOT_PRESENT) == 1)))
853         {
854           rv = 0;
855           goto cleanup;
856         }
857 
858       pstdout_fprintf (state_data->pstate,
859                        stderr,
860                        "ipmi_cmd_dcmi_get_dcmi_capability_info_enhanced_system_power_statistics_attributes: %s\n",
861                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
862       goto cleanup;
863     }
864 
865   if (FIID_OBJ_GET (obj_cmd_rs,
866                     "number_of_supported_rolling_average_time_periods",
867                     &val) < 0)
868     {
869       pstdout_fprintf (state_data->pstate,
870                        stderr,
871                        "fiid_obj_get: 'number_of_supported_rolling_average_time_periods': %s\n",
872                        fiid_obj_errormsg (obj_cmd_rs));
873       goto cleanup;
874     }
875   (*number_of_supported_rolling_average_time_periods) = val;
876 
877   if ((len = fiid_obj_get_data (obj_cmd_rs,
878                                 "rolling_average_time_periods",
879                                 rolling_average_time_periods,
880                                 rolling_average_time_periods_buflen)) < 0)
881     {
882       pstdout_fprintf (state_data->pstate,
883                        stderr,
884                        "fiid_obj_get_data: 'rolling_average_time_periods': %s\n",
885                        fiid_obj_errormsg (obj_cmd_rs));
886       goto cleanup;
887     }
888 
889   if ((*number_of_supported_rolling_average_time_periods) != len)
890     {
891       pstdout_fprintf (state_data->pstate,
892                        stderr,
893                        "invalid number of supported rolling average time periods reported: %u vs. %u\n",
894                        (*number_of_supported_rolling_average_time_periods),
895                        len);
896       goto cleanup;
897     }
898 
899   if ((len = fiid_template_len_bytes (tmpl_dcmi_rolling_average_time_period)) < 0)
900     {
901       pstdout_fprintf (state_data->pstate,
902                        stderr,
903                        "fiid_template_len_bytes: %s\n",
904                        strerror (errno));
905       goto cleanup;
906     }
907 
908   /* an "assertion" */
909   if (len != 1)
910     {
911       pstdout_fprintf (state_data->pstate,
912                        stderr,
913                        "rolling average time period length invalid: %u\n",
914                        len);
915       goto cleanup;
916     }
917 
918   rv = 1;
919  cleanup:
920   fiid_obj_destroy (obj_cmd_rs);
921   return (rv);
922 }
923 
924 static int
925 _get_time_duration_info (ipmi_dcmi_state_data_t *state_data,
926                          uint8_t rolling_average_time_period,
927                          uint8_t *time_duration,
928                          char **time_duration_units_str)
929 {
930   fiid_obj_t obj_rolling_average_time_period = NULL;
931   uint8_t time_duration_units;
932   uint64_t val;
933   int rv = -1;
934 
935   assert (state_data);
936   assert (time_duration);
937   assert (time_duration_units_str);
938 
939   if (!(obj_rolling_average_time_period = fiid_obj_create (tmpl_dcmi_rolling_average_time_period)))
940     {
941       pstdout_fprintf (state_data->pstate,
942                        stderr,
943                        "fiid_obj_create: %s\n",
944                        strerror (errno));
945       goto cleanup;
946     }
947 
948   if (fiid_obj_set_all (obj_rolling_average_time_period,
949                         &rolling_average_time_period,
950                         1) < 0)
951     {
952       pstdout_fprintf (state_data->pstate,
953                        stderr,
954                        "fiid_obj_set_all: %s\n",
955                        fiid_obj_errormsg (obj_rolling_average_time_period));
956       goto cleanup;
957     }
958 
959   if (FIID_OBJ_GET (obj_rolling_average_time_period,
960                     "time_duration",
961                     &val) < 0)
962     {
963       pstdout_fprintf (state_data->pstate,
964                        stderr,
965                        "fiid_obj_get: 'time_duration': %s\n",
966                        fiid_obj_errormsg (obj_rolling_average_time_period));
967       goto cleanup;
968     }
969   (*time_duration) = val;
970 
971   if (FIID_OBJ_GET (obj_rolling_average_time_period,
972                     "time_duration_units",
973                     &val) < 0)
974     {
975       pstdout_fprintf (state_data->pstate,
976                        stderr,
977                        "fiid_obj_get: 'time_duration_units': %s\n",
978                        fiid_obj_errormsg (obj_rolling_average_time_period));
979       goto cleanup;
980     }
981   time_duration_units = val;
982 
983   if (time_duration_units == IPMI_DCMI_TIME_DURATION_UNITS_SECONDS)
984     (*time_duration_units_str) = "Seconds";
985   else if (time_duration_units == IPMI_DCMI_TIME_DURATION_UNITS_MINUTES)
986     (*time_duration_units_str) = "Minutes";
987   else if (time_duration_units == IPMI_DCMI_TIME_DURATION_UNITS_HOURS)
988     (*time_duration_units_str) = "Hours";
989   else
990     (*time_duration_units_str) = "Days";
991 
992   rv = 0;
993  cleanup:
994   fiid_obj_destroy (obj_rolling_average_time_period);
995   return (rv);
996 }
997 
998 /* return 1 on output success, 0 on no output, -1 on error */
999 static int
1000 _enhanced_system_power_statistics_attributes (ipmi_dcmi_state_data_t *state_data)
1001 {
1002   uint8_t rolling_average_time_periods[IPMI_DCMI_ROLLING_AVERAGE_TIME_PERIOD_BUFLEN];
1003   uint8_t number_of_supported_rolling_average_time_periods;
1004   int i;
1005 
1006   assert (state_data);
1007 
1008   if (_get_enhanced_system_power_statistics_attributes (state_data,
1009                                                         &number_of_supported_rolling_average_time_periods,
1010                                                         rolling_average_time_periods,
1011                                                         IPMI_DCMI_ROLLING_AVERAGE_TIME_PERIOD_BUFLEN) < 0)
1012     return (-1);
1013 
1014   for (i = 0; i < number_of_supported_rolling_average_time_periods; i++)
1015     {
1016       uint8_t time_duration;
1017       char *time_duration_units_str = NULL;
1018 
1019       if (_get_time_duration_info (state_data,
1020                                    rolling_average_time_periods[i],
1021                                    &time_duration,
1022                                    &time_duration_units_str) < 0)
1023         return (-1);
1024 
1025       pstdout_printf (state_data->pstate,
1026                       "Available Rolling Average Time Period              : %u %s\n",
1027                       time_duration,
1028                       time_duration_units_str);
1029     }
1030 
1031   return (1);
1032 }
1033 
1034 static int
1035 get_dcmi_capability_info (ipmi_dcmi_state_data_t *state_data)
1036 {
1037   uint8_t parameter_revision;
1038   int ret;
1039 
1040   assert (state_data);
1041 
1042   if ((ret = _dcmi_specification_conformance (state_data, &parameter_revision)) < 0)
1043     return (-1);
1044 
1045   if (ret)
1046     pstdout_printf (state_data->pstate, "\n");
1047 
1048   if ((ret = _supported_dcmi_capabilities (state_data)) < 0)
1049     return (-1);
1050 
1051   if (ret)
1052     pstdout_printf (state_data->pstate, "\n");
1053 
1054   if ((ret = _mandatory_platform_attributes (state_data)) < 0)
1055     return (-1);
1056 
1057   if (ret)
1058     pstdout_printf (state_data->pstate, "\n");
1059 
1060   if ((ret = _optional_platform_attributes (state_data)) < 0)
1061     return (-1);
1062 
1063   if (ret)
1064     pstdout_printf (state_data->pstate, "\n");
1065 
1066   if ((ret = _manageability_access_attributes (state_data)) < 0)
1067     return (-1);
1068 
1069   if (parameter_revision >= 0x02)
1070     {
1071       if (ret)
1072         pstdout_printf (state_data->pstate, "\n");
1073 
1074       if ((ret = _enhanced_system_power_statistics_attributes (state_data)) < 0)
1075         return (-1);
1076     }
1077 
1078   return (0);
1079 }
1080 
1081 static int
1082 get_asset_tag (ipmi_dcmi_state_data_t *state_data)
1083 {
1084   fiid_obj_t obj_cmd_rs = NULL;
1085   uint8_t asset_tag_data[IPMI_DCMI_MAX_ASSET_TAG_LENGTH + 1];
1086   int data_len;
1087   unsigned int offset = 0;
1088   uint8_t total_asset_tag_length = 0;
1089   uint8_t bytes_to_read = IPMI_DCMI_ASSET_TAG_NUMBER_OF_BYTES_TO_READ_MAX;
1090   int rv = -1;
1091 
1092   assert (state_data);
1093 
1094   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_asset_tag_rs)))
1095     {
1096       pstdout_fprintf (state_data->pstate,
1097                        stderr,
1098                        "fiid_obj_create: %s\n",
1099                        strerror (errno));
1100       goto cleanup;
1101     }
1102 
1103   memset (asset_tag_data, '\0', IPMI_DCMI_MAX_ASSET_TAG_LENGTH + 1);
1104 
1105   while (1)
1106     {
1107       uint64_t val;
1108 
1109       if (!offset
1110           || ((total_asset_tag_length - offset) >= IPMI_DCMI_ASSET_TAG_NUMBER_OF_BYTES_TO_READ_MAX))
1111         bytes_to_read = IPMI_DCMI_ASSET_TAG_NUMBER_OF_BYTES_TO_READ_MAX;
1112       else
1113         bytes_to_read = total_asset_tag_length - offset;
1114 
1115       if (ipmi_cmd_dcmi_get_asset_tag (state_data->ipmi_ctx,
1116                                        offset,
1117                                        bytes_to_read,
1118                                        obj_cmd_rs) < 0)
1119         {
1120           pstdout_fprintf (state_data->pstate,
1121                            stderr,
1122                            "ipmi_cmd_dcmi_get_asset_tag: %s\n",
1123                            ipmi_ctx_errormsg (state_data->ipmi_ctx));
1124           goto cleanup;
1125         }
1126 
1127       if (FIID_OBJ_GET (obj_cmd_rs,
1128                         "total_asset_tag_length",
1129                         &val) < 0)
1130         {
1131           pstdout_fprintf (state_data->pstate,
1132                            stderr,
1133                            "fiid_obj_get: 'total_asset_tag_length': %s\n",
1134                            fiid_obj_errormsg (obj_cmd_rs));
1135           goto cleanup;
1136         }
1137       total_asset_tag_length = val;
1138 
1139       if (!total_asset_tag_length)
1140         break;
1141 
1142       if ((data_len = fiid_obj_get_data (obj_cmd_rs,
1143                                          "data",
1144                                          asset_tag_data + offset,
1145                                          IPMI_DCMI_MAX_ASSET_TAG_LENGTH - offset)) < 0)
1146         {
1147           pstdout_fprintf (state_data->pstate,
1148                            stderr,
1149                            "fiid_obj_get_data: 'data': %s\n",
1150                            fiid_obj_errormsg (obj_cmd_rs));
1151           goto cleanup;
1152         }
1153       offset += data_len;
1154 
1155       if (offset >= total_asset_tag_length)
1156         break;
1157     }
1158 
1159   if (total_asset_tag_length)
1160     {
1161       /* Handle special case UTF-8 encoding w/ BOM prefix */
1162       if (asset_tag_data[0] == IPMI_DCMI_ASSET_TAG_UTF8_BOM_BYTE0
1163           && asset_tag_data[1] == IPMI_DCMI_ASSET_TAG_UTF8_BOM_BYTE1
1164           && asset_tag_data[2] == IPMI_DCMI_ASSET_TAG_UTF8_BOM_BYTE2)
1165         /* achu: I think this is right for UTF-8 in libc and is
1166          * portable, but I would bet some systems won't like this.
1167          */
1168         pstdout_printf (state_data->pstate,
1169                         "%ls\n",
1170                         &asset_tag_data[3]);
1171       else
1172         pstdout_printf (state_data->pstate,
1173                         "%s\n",
1174                         asset_tag_data);
1175     }
1176 
1177   rv = 0;
1178  cleanup:
1179   fiid_obj_destroy (obj_cmd_rs);
1180   return (rv);
1181 }
1182 
1183 static int
1184 set_asset_tag (ipmi_dcmi_state_data_t *state_data)
1185 {
1186   fiid_obj_t obj_cmd_rs = NULL;
1187   unsigned int offset = 0;
1188   char data_buf[IPMI_DCMI_MAX_ASSET_TAG_LENGTH + 1];
1189   unsigned int data_len;
1190   uint8_t bytes_to_write = IPMI_DCMI_ASSET_TAG_NUMBER_OF_BYTES_TO_WRITE_MAX;
1191   int rv = -1;
1192 
1193   assert (state_data);
1194 
1195   /* achu:
1196    *
1197    * DCMI v1.1 spec is unclear if the entire buffer needs to be
1198    * written or just the amount you desire.
1199    *
1200    * DCMI v1.5 spec strongly suggests you don't write the entire
1201    * buffer due to the results of the "total_asset_tag_length_written"
1202    * field.
1203    *
1204    * "Total Asset Tag Length. This is the length in bytes of the stored
1205    * Asset Tag after the Set operation has completed. The Asset Tag
1206    * length shall be set to the sum of the offset to write plus bytes
1207    * to write. For example, if offset to write is 32 and bytes to
1208    * write is 4, the Total Asset Tag Length returned will be 36."
1209    */
1210 
1211   data_len = strlen (state_data->prog_data->args->set_asset_tag_arg);
1212 
1213   memset (data_buf, '\0', IPMI_DCMI_MAX_ASSET_TAG_LENGTH + 1);
1214 
1215   memcpy (data_buf,
1216           state_data->prog_data->args->set_asset_tag_arg,
1217           strlen (state_data->prog_data->args->set_asset_tag_arg));
1218 
1219   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_set_asset_tag_rs)))
1220     {
1221       pstdout_fprintf (state_data->pstate,
1222                        stderr,
1223                        "fiid_obj_create: %s\n",
1224                        strerror (errno));
1225       goto cleanup;
1226     }
1227 
1228   while (1)
1229     {
1230       uint64_t val;
1231 
1232       if ((data_len - offset) >= IPMI_DCMI_ASSET_TAG_NUMBER_OF_BYTES_TO_WRITE_MAX)
1233         bytes_to_write = IPMI_DCMI_ASSET_TAG_NUMBER_OF_BYTES_TO_WRITE_MAX;
1234       else
1235         bytes_to_write = data_len - offset;
1236 
1237       if (ipmi_cmd_dcmi_set_asset_tag (state_data->ipmi_ctx,
1238                                        offset,
1239                                        bytes_to_write,
1240                                        data_buf + offset,
1241                                        data_len,
1242                                        obj_cmd_rs) < 0)
1243         {
1244           pstdout_fprintf (state_data->pstate,
1245                            stderr,
1246                            "ipmi_cmd_dcmi_set_asset_tag: %s\n",
1247                            ipmi_ctx_errormsg (state_data->ipmi_ctx));
1248           goto cleanup;
1249         }
1250 
1251       if (FIID_OBJ_GET (obj_cmd_rs,
1252                         "total_asset_tag_length_written",
1253                         &val) < 0)
1254         {
1255           pstdout_fprintf (state_data->pstate,
1256                            stderr,
1257                            "fiid_obj_get: 'total_asset_tag_length': %s\n",
1258                            fiid_obj_errormsg (obj_cmd_rs));
1259           goto cleanup;
1260         }
1261 
1262       /* DCMI 1.1 spec is unclear on "total_length_written", is it the
1263        * number of bytes just written or total bytes written so far?
1264        *
1265        * DCMI 1.5 spec makes it clear that this is the number of bytes
1266        * written in total.  To defend against vendor mistakes, we
1267        * handle both situations.
1268        */
1269       if (val > bytes_to_write)
1270         offset += bytes_to_write;
1271       else
1272         offset += val;
1273 
1274       if (offset >= data_len)
1275         break;
1276     }
1277 
1278   rv = 0;
1279  cleanup:
1280   fiid_obj_destroy (obj_cmd_rs);
1281   return (rv);
1282 }
1283 
1284 static int
1285 get_management_controller_identifier_string (ipmi_dcmi_state_data_t *state_data)
1286 {
1287   fiid_obj_t obj_cmd_rs = NULL;
1288   char management_controller_identifier_string_data[IPMI_DCMI_MAX_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_LENGTH + 1];
1289   int data_len;
1290   unsigned int offset = 0;
1291   uint8_t total_length = 0;
1292   uint8_t bytes_to_read = IPMI_DCMI_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_NUMBER_OF_BYTES_TO_READ_MAX;
1293   int rv = -1;
1294 
1295   assert (state_data);
1296 
1297   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_management_controller_identifier_string_rs)))
1298     {
1299       pstdout_fprintf (state_data->pstate,
1300                        stderr,
1301                        "fiid_obj_create: %s\n",
1302                        strerror (errno));
1303       goto cleanup;
1304     }
1305 
1306   memset (management_controller_identifier_string_data, '\0', IPMI_DCMI_MAX_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_LENGTH + 1);
1307 
1308   while (1)
1309     {
1310       uint64_t val;
1311 
1312       if (!offset
1313           || ((total_length - offset) >= IPMI_DCMI_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_NUMBER_OF_BYTES_TO_READ_MAX))
1314         bytes_to_read = IPMI_DCMI_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_NUMBER_OF_BYTES_TO_READ_MAX;
1315       else
1316         bytes_to_read = total_length - offset;
1317 
1318       if (ipmi_cmd_dcmi_get_management_controller_identifier_string (state_data->ipmi_ctx,
1319                                                                      offset,
1320                                                                      bytes_to_read,
1321                                                                      obj_cmd_rs) < 0)
1322         {
1323           pstdout_fprintf (state_data->pstate,
1324                            stderr,
1325                            "ipmi_cmd_dcmi_get_management_controller_identifier_string: %s\n",
1326                            ipmi_ctx_errormsg (state_data->ipmi_ctx));
1327           goto cleanup;
1328         }
1329 
1330       if (FIID_OBJ_GET (obj_cmd_rs,
1331                         "total_length",
1332                         &val) < 0)
1333         {
1334           pstdout_fprintf (state_data->pstate,
1335                            stderr,
1336                            "fiid_obj_get: 'total_length': %s\n",
1337                            fiid_obj_errormsg (obj_cmd_rs));
1338           goto cleanup;
1339         }
1340       total_length = val;
1341 
1342       if (!total_length)
1343         break;
1344 
1345       if ((data_len = fiid_obj_get_data (obj_cmd_rs,
1346                                          "data",
1347                                          management_controller_identifier_string_data + offset,
1348                                          IPMI_DCMI_MAX_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_LENGTH - offset)) < 0)
1349         {
1350           pstdout_fprintf (state_data->pstate,
1351                            stderr,
1352                            "fiid_obj_get_data: 'data': %s\n",
1353                            fiid_obj_errormsg (obj_cmd_rs));
1354           goto cleanup;
1355         }
1356       offset += data_len;
1357 
1358       if (offset >= total_length)
1359         break;
1360     }
1361 
1362   if (total_length)
1363     pstdout_printf (state_data->pstate,
1364                     "%s\n",
1365                     management_controller_identifier_string_data);
1366 
1367   rv = 0;
1368  cleanup:
1369   fiid_obj_destroy (obj_cmd_rs);
1370   return (rv);
1371 }
1372 
1373 static int
1374 set_management_controller_identifier_string (ipmi_dcmi_state_data_t *state_data)
1375 {
1376   fiid_obj_t obj_cmd_rs = NULL;
1377   unsigned int offset = 0;
1378   char data_buf[IPMI_DCMI_MAX_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_LENGTH + 1];
1379   unsigned int data_len;
1380   uint8_t bytes_to_write = IPMI_DCMI_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_NUMBER_OF_BYTES_TO_WRITE_MAX;
1381   int rv = -1;
1382 
1383   assert (state_data);
1384 
1385   /* achu:
1386    *
1387    * According to DCMI v1.5 draft
1388    *
1389    * "The presence of the null terminator among bytes to shall be
1390    * considered as indicating the last transfer of the Management
1391    * Controller Identifier string"
1392    *
1393    * So I am assuming we don't need to write the entire buffer.  But
1394    * we must include the NUL byte at the end.
1395    */
1396 
1397   /* +1 for NUL char */
1398   data_len = strlen (state_data->prog_data->args->set_management_controller_identifier_string_arg) + 1;
1399 
1400   memset (data_buf, '\0', IPMI_DCMI_MAX_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_LENGTH + 1);
1401 
1402   memcpy (data_buf,
1403           state_data->prog_data->args->set_management_controller_identifier_string_arg,
1404           strlen (state_data->prog_data->args->set_management_controller_identifier_string_arg));
1405 
1406   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_set_management_controller_identifier_string_rs)))
1407     {
1408       pstdout_fprintf (state_data->pstate,
1409                        stderr,
1410                        "fiid_obj_create: %s\n",
1411                        strerror (errno));
1412       goto cleanup;
1413     }
1414 
1415   while (1)
1416     {
1417       uint64_t val;
1418 
1419       if ((data_len - offset) >= IPMI_DCMI_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_NUMBER_OF_BYTES_TO_WRITE_MAX)
1420         bytes_to_write = IPMI_DCMI_MANAGEMENT_CONTROLLER_IDENTIFIER_STRING_NUMBER_OF_BYTES_TO_WRITE_MAX;
1421       else
1422         bytes_to_write = data_len - offset;
1423 
1424       if (ipmi_cmd_dcmi_set_management_controller_identifier_string (state_data->ipmi_ctx,
1425                                                                      offset,
1426                                                                      bytes_to_write,
1427                                                                      data_buf + offset,
1428                                                                      data_len,
1429                                                                      obj_cmd_rs) < 0)
1430         {
1431           pstdout_fprintf (state_data->pstate,
1432                            stderr,
1433                            "ipmi_cmd_dcmi_set_management_controller_identifier_string: %s\n",
1434                            ipmi_ctx_errormsg (state_data->ipmi_ctx));
1435           goto cleanup;
1436         }
1437 
1438       if (FIID_OBJ_GET (obj_cmd_rs,
1439                         "total_length_written",
1440                         &val) < 0)
1441         {
1442           pstdout_fprintf (state_data->pstate,
1443                            stderr,
1444                            "fiid_obj_get: 'total_management_controller_identifier_string_length': %s\n",
1445                            fiid_obj_errormsg (obj_cmd_rs));
1446           goto cleanup;
1447         }
1448 
1449 
1450       /* DCMI 1.1 spec is unclear on "total_length_written", is it the
1451        * number of bytes just written or total bytes written so far?
1452        *
1453        * DCMI 1.5 spec makes it clear that this is the number of bytes
1454        * written in total.  To defend against vendor mistakes, we
1455        * handle both situations.
1456        */
1457       if (val > bytes_to_write)
1458         offset += bytes_to_write;
1459       else
1460         offset += val;
1461 
1462       if (offset >= data_len)
1463         break;
1464     }
1465 
1466   rv = 0;
1467  cleanup:
1468   fiid_obj_destroy (obj_cmd_rs);
1469   return (rv);
1470 }
1471 
1472 static char *
1473 _entity_string (uint8_t entity_id)
1474 {
1475   assert (IPMI_DCMI_ENTITY_ID_VALID (entity_id));
1476 
1477   if (entity_id == IPMI_DCMI_ENTITY_ID_INLET_TEMPERATURE)
1478     return IPMI_DCMI_ENTITY_ID_INLET_TEMPERATURE_STR;
1479   else if (entity_id == IPMI_DCMI_ENTITY_ID_CPU_TEMPERATURE)
1480     return IPMI_DCMI_ENTITY_ID_CPU_TEMPERATURE_STR;
1481   else
1482     return IPMI_DCMI_ENTITY_ID_BASEBOARD_TEMPERATURE_STR;
1483 }
1484 
1485 static int
1486 _sensor_info_output (ipmi_dcmi_state_data_t *state_data,
1487                      uint8_t sensor_type,
1488                      uint8_t entity_id)
1489 {
1490   fiid_obj_t obj_cmd_rs = NULL;
1491   uint8_t entity_instance_start = 0x01; /* starts at 1, not 0 */
1492   unsigned int total_entity_instances_parsed = 0;
1493   int rv = -1;
1494 
1495   assert (state_data);
1496   assert (IPMI_SENSOR_TYPE_VALID (sensor_type));
1497   assert (IPMI_DCMI_ENTITY_ID_VALID (entity_id));
1498 
1499   pstdout_printf (state_data->pstate,
1500                   "%s SDR Record IDs\n",
1501                   _entity_string (entity_id));
1502 
1503   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_dcmi_sensor_info_rs)))
1504     {
1505       pstdout_fprintf (state_data->pstate,
1506                        stderr,
1507                        "fiid_obj_create: %s\n",
1508                        strerror (errno));
1509       goto cleanup;
1510     }
1511 
1512   while (1)
1513     {
1514       uint64_t val;
1515       uint8_t total_number_of_available_instances;
1516       uint8_t number_of_record_ids_in_this_response;
1517       uint8_t sdr_record_ids[IPMI_DCMI_MAX_RECORD_IDS_BUFLEN];
1518       int sdr_record_ids_len;
1519       int i;
1520 
1521       if (ipmi_cmd_dcmi_get_dcmi_sensor_info (state_data->ipmi_ctx,
1522                                               sensor_type,
1523                                               entity_id,
1524                                               IPMI_DCMI_ENTITY_INSTANCE_ALL,
1525                                               entity_instance_start,
1526                                               obj_cmd_rs) < 0)
1527         {
1528           pstdout_fprintf (state_data->pstate,
1529                            stderr,
1530                            "ipmi_cmd_dcmi_get_dcmi_sensor_info: %s\n",
1531                            ipmi_ctx_errormsg (state_data->ipmi_ctx));
1532           goto cleanup;
1533         }
1534 
1535       if (FIID_OBJ_GET (obj_cmd_rs,
1536                         "total_number_of_available_instances",
1537                         &val) < 0)
1538         {
1539           pstdout_fprintf (state_data->pstate,
1540                            stderr,
1541                            "fiid_obj_get: 'total_number_of_available_instances': %s\n",
1542                            fiid_obj_errormsg (obj_cmd_rs));
1543           goto cleanup;
1544         }
1545       total_number_of_available_instances = val;
1546 
1547       if (!total_number_of_available_instances)
1548         break;
1549 
1550       if (FIID_OBJ_GET (obj_cmd_rs,
1551                         "number_of_record_ids_in_this_response",
1552                         &val) < 0)
1553         {
1554           pstdout_fprintf (state_data->pstate,
1555                            stderr,
1556                            "fiid_obj_get: 'number_of_record_ids_in_this_response': %s\n",
1557                            fiid_obj_errormsg (obj_cmd_rs));
1558           goto cleanup;
1559         }
1560       number_of_record_ids_in_this_response = val;
1561 
1562       if (!number_of_record_ids_in_this_response)
1563         break;
1564 
1565       if ((sdr_record_ids_len = fiid_obj_get_data (obj_cmd_rs,
1566                                                    "sdr_record_ids",
1567                                                    sdr_record_ids,
1568                                                    IPMI_DCMI_MAX_RECORD_IDS_BUFLEN)) < 0)
1569         {
1570           pstdout_fprintf (state_data->pstate,
1571                            stderr,
1572                            "fiid_obj_get_data: 'sdr_record_ids': %s\n",
1573                            fiid_obj_errormsg (obj_cmd_rs));
1574           goto cleanup;
1575         }
1576 
1577       if (sdr_record_ids_len % 2)
1578         {
1579           pstdout_fprintf (state_data->pstate,
1580                            stderr,
1581                            "invalid sdr_record_ids length returned: %u\n",
1582                            sdr_record_ids_len);
1583           goto cleanup;
1584         }
1585 
1586       if (number_of_record_ids_in_this_response > (sdr_record_ids_len / 2))
1587         {
1588           pstdout_fprintf (state_data->pstate,
1589                            stderr,
1590                            "invalid sdr_record_ids returned: %u > %u\n",
1591                            number_of_record_ids_in_this_response,
1592                            (sdr_record_ids_len / 2));
1593           goto cleanup;
1594         }
1595 
1596       for (i = 0; i < (number_of_record_ids_in_this_response * 2); i += 2)
1597         {
1598           uint16_t record_id = 0;
1599 
1600           record_id |= sdr_record_ids[i];
1601           record_id |= (sdr_record_ids[i+1] << 8);
1602 
1603           pstdout_printf (state_data->pstate,
1604                           "%u\n",
1605                           record_id);
1606           total_entity_instances_parsed++;
1607         }
1608 
1609       /* achu: entity IDs are returned sequentially?  If not, I'm not
1610        * sure how this API can even work, you wouldn't know where to
1611        * start the next time around.  Hopefully this is a correct
1612        * assumption
1613        */
1614       /* HLiebig: Note: Intel simply increments the offset by 8 (max number of
1615        * SDR Id's per response.
1616        * See dcmitool from www.intel.com/go/DCMI (a modified ipmitool)
1617        */
1618 
1619       entity_instance_start += number_of_record_ids_in_this_response;
1620 
1621       if (total_entity_instances_parsed >= total_number_of_available_instances)
1622         break;
1623     }
1624 
1625   rv = 0;
1626  cleanup:
1627   fiid_obj_destroy (obj_cmd_rs);
1628   return (rv);
1629 }
1630 
1631 static int
1632 get_dcmi_sensor_info (ipmi_dcmi_state_data_t *state_data)
1633 {
1634   int rv = -1;
1635 
1636   assert (state_data);
1637 
1638   if (_sensor_info_output (state_data,
1639                            IPMI_SENSOR_TYPE_TEMPERATURE,
1640                            IPMI_DCMI_ENTITY_ID_INLET_TEMPERATURE) < 0)
1641     goto cleanup;
1642 
1643   pstdout_printf (state_data->pstate, "\n");
1644 
1645   if (_sensor_info_output (state_data,
1646                            IPMI_SENSOR_TYPE_TEMPERATURE,
1647                            IPMI_DCMI_ENTITY_ID_CPU_TEMPERATURE) < 0)
1648     goto cleanup;
1649 
1650   pstdout_printf (state_data->pstate, "\n");
1651 
1652   if (_sensor_info_output (state_data,
1653                            IPMI_SENSOR_TYPE_TEMPERATURE,
1654                            IPMI_DCMI_ENTITY_ID_BASEBOARD_TEMPERATURE) < 0)
1655     goto cleanup;
1656 
1657   rv = 0;
1658  cleanup:
1659   return (rv);
1660 }
1661 
1662 static int
1663 _output_power_statistics (ipmi_dcmi_state_data_t *state_data,
1664                           uint8_t mode,
1665                           uint8_t mode_attributes)
1666 {
1667   fiid_obj_t obj_cmd_rs = NULL;
1668   uint16_t current_power;
1669   uint16_t minimum_power_over_sampling_duration;
1670   uint16_t maximum_power_over_sampling_duration;
1671   uint16_t average_power_over_sampling_duration;
1672   uint32_t time_stamp;
1673   uint32_t statistics_reporting_time_period;
1674   uint8_t power_measurement;
1675   uint64_t val;
1676   char timestr[IPMI_DCMI_TIME_BUFLEN + 1];
1677   int rv = -1;
1678 
1679   assert (state_data);
1680 
1681   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_power_reading_rs)))
1682     {
1683       pstdout_fprintf (state_data->pstate,
1684                        stderr,
1685                        "fiid_obj_create: %s\n",
1686                        strerror (errno));
1687       goto cleanup;
1688     }
1689 
1690   if (ipmi_cmd_dcmi_get_power_reading (state_data->ipmi_ctx,
1691                                        mode,
1692                                        mode_attributes,
1693                                        obj_cmd_rs) < 0)
1694     {
1695       pstdout_fprintf (state_data->pstate,
1696                        stderr,
1697                        "ipmi_cmd_dcmi_get_power_reading: %s\n",
1698                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
1699       goto cleanup;
1700     }
1701 
1702   if (FIID_OBJ_GET (obj_cmd_rs,
1703                     "current_power",
1704                     &val) < 0)
1705     {
1706       pstdout_fprintf (state_data->pstate,
1707                        stderr,
1708                        "fiid_obj_get: 'current_power': %s\n",
1709                        fiid_obj_errormsg (obj_cmd_rs));
1710       goto cleanup;
1711     }
1712   current_power = val;
1713 
1714   if (FIID_OBJ_GET (obj_cmd_rs,
1715                     "minimum_power_over_sampling_duration",
1716                     &val) < 0)
1717     {
1718       pstdout_fprintf (state_data->pstate,
1719                        stderr,
1720                        "fiid_obj_get: 'minimum_power_over_sampling_duration': %s\n",
1721                        fiid_obj_errormsg (obj_cmd_rs));
1722       goto cleanup;
1723     }
1724   minimum_power_over_sampling_duration = val;
1725 
1726   if (FIID_OBJ_GET (obj_cmd_rs,
1727                     "maximum_power_over_sampling_duration",
1728                     &val) < 0)
1729     {
1730       pstdout_fprintf (state_data->pstate,
1731                        stderr,
1732                        "fiid_obj_get: 'maximum_power_over_sampling_duration': %s\n",
1733                        fiid_obj_errormsg (obj_cmd_rs));
1734       goto cleanup;
1735     }
1736   maximum_power_over_sampling_duration = val;
1737 
1738   if (FIID_OBJ_GET (obj_cmd_rs,
1739                     "average_power_over_sampling_duration",
1740                     &val) < 0)
1741     {
1742       pstdout_fprintf (state_data->pstate,
1743                        stderr,
1744                        "fiid_obj_get: 'average_power_over_sampling_duration': %s\n",
1745                        fiid_obj_errormsg (obj_cmd_rs));
1746       goto cleanup;
1747     }
1748   average_power_over_sampling_duration = val;
1749 
1750   if (FIID_OBJ_GET (obj_cmd_rs,
1751                     "time_stamp",
1752                     &val) < 0)
1753     {
1754       pstdout_fprintf (state_data->pstate,
1755                        stderr,
1756                        "fiid_obj_get: 'time_stamp': %s\n",
1757                        fiid_obj_errormsg (obj_cmd_rs));
1758       goto cleanup;
1759     }
1760   time_stamp = val;
1761 
1762   if (FIID_OBJ_GET (obj_cmd_rs,
1763                     "statistics_reporting_time_period",
1764                     &val) < 0)
1765     {
1766       pstdout_fprintf (state_data->pstate,
1767                        stderr,
1768                        "fiid_obj_get: 'statistics_reporting_time_period': %s\n",
1769                        fiid_obj_errormsg (obj_cmd_rs));
1770       goto cleanup;
1771     }
1772   statistics_reporting_time_period = val;
1773 
1774   if (FIID_OBJ_GET (obj_cmd_rs,
1775                     "power_reading_state.power_measurement",
1776                     &val) < 0)
1777     {
1778       pstdout_fprintf (state_data->pstate,
1779                        stderr,
1780                        "fiid_obj_get: 'power_measurement': %s\n",
1781                        fiid_obj_errormsg (obj_cmd_rs));
1782       goto cleanup;
1783     }
1784   power_measurement = val;
1785 
1786   pstdout_printf (state_data->pstate,
1787                   "Current Power                        : %u Watts\n",
1788                   current_power);
1789 
1790   pstdout_printf (state_data->pstate,
1791                   "Minimum Power over sampling duration : %u watts\n",
1792                   minimum_power_over_sampling_duration);
1793 
1794   pstdout_printf (state_data->pstate,
1795                   "Maximum Power over sampling duration : %u watts\n",
1796                   maximum_power_over_sampling_duration);
1797 
1798   pstdout_printf (state_data->pstate,
1799                   "Average Power over sampling duration : %u watts\n",
1800                   average_power_over_sampling_duration);
1801 
1802   memset (timestr, '\0', IPMI_DCMI_TIME_BUFLEN + 1);
1803 
1804   if (ipmi_timestamp_string (time_stamp,
1805                              state_data->prog_data->args->common_args.utc_offset,
1806                              get_timestamp_flags (&(state_data->prog_data->args->common_args),
1807                                                   IPMI_TIMESTAMP_FLAG_DEFAULT),
1808                              "%m/%d/%Y - %H:%M:%S",
1809                              timestr,
1810                              IPMI_DCMI_TIME_BUFLEN) < 0)
1811     {
1812       pstdout_fprintf (state_data->pstate,
1813                        stderr,
1814                        "ipmi_timestamp_string: %s\n",
1815                        strerror (errno));
1816       goto cleanup;
1817     }
1818 
1819   pstdout_printf (state_data->pstate,
1820                   "Time Stamp                           : %s\n",
1821                   timestr);
1822 
1823   pstdout_printf (state_data->pstate,
1824                   "Statistics reporting time period     : %u milliseconds\n",
1825                   statistics_reporting_time_period);
1826 
1827   pstdout_printf (state_data->pstate,
1828                   "Power Measurement                    : %s\n",
1829                   (power_measurement) ? "Active" : "Not Available");
1830 
1831   rv = 0;
1832  cleanup:
1833   fiid_obj_destroy (obj_cmd_rs);
1834   return (rv);
1835 }
1836 
1837 static int
1838 get_system_power_statistics (ipmi_dcmi_state_data_t *state_data)
1839 {
1840   assert (state_data);
1841 
1842   if (_output_power_statistics (state_data,
1843                                 IPMI_DCMI_POWER_READING_MODE_SYSTEM_POWER_STATISTICS,
1844                                 0) < 0)
1845     return (-1);
1846 
1847   return (0);
1848 }
1849 
1850 static int
1851 get_enhanced_system_power_statistics (ipmi_dcmi_state_data_t *state_data)
1852 {
1853   uint8_t rolling_average_time_periods[IPMI_DCMI_ROLLING_AVERAGE_TIME_PERIOD_BUFLEN];
1854   uint8_t number_of_supported_rolling_average_time_periods = 0;
1855   int i;
1856 
1857   assert (state_data);
1858 
1859   if (_get_enhanced_system_power_statistics_attributes (state_data,
1860                                                         &number_of_supported_rolling_average_time_periods,
1861                                                         rolling_average_time_periods,
1862                                                         IPMI_DCMI_ROLLING_AVERAGE_TIME_PERIOD_BUFLEN) < 0)
1863     return (-1);
1864 
1865 
1866   for (i = 0; i < number_of_supported_rolling_average_time_periods; i++)
1867     {
1868       uint8_t time_duration;
1869       char *time_duration_units_str = NULL;
1870 
1871       if (_get_time_duration_info (state_data,
1872                                    rolling_average_time_periods[i],
1873                                    &time_duration,
1874                                    &time_duration_units_str) < 0)
1875         return (-1);
1876 
1877       pstdout_printf (state_data->pstate,
1878                       "Power Statistics for Rolling Average Time Period %u %s\n",
1879                       time_duration,
1880                       time_duration_units_str);
1881 
1882       pstdout_printf (state_data->pstate, "\n");
1883 
1884       if (_output_power_statistics (state_data,
1885                                     IPMI_DCMI_POWER_READING_MODE_ENHANCED_SYSTEM_POWER_STATISTICS,
1886                                     rolling_average_time_periods[i]) < 0)
1887         return (-1);
1888     }
1889 
1890   return (0);
1891 }
1892 
1893 static int
1894 _get_power_limit (ipmi_dcmi_state_data_t *state_data,
1895                   uint8_t *exception_actions,
1896                   uint16_t *power_limit_requested,
1897                   uint32_t *correction_time_limit,
1898                   uint16_t *management_application_statistics_sampling_period,
1899                   uint8_t *comp_code,
1900                   char *errorbuf,
1901                   unsigned int errorbuflen)
1902 
1903 {
1904   fiid_obj_t obj_cmd_rs = NULL;
1905   uint64_t val;
1906   int no_set_power_limit_error_flag = 0;
1907   int rv = -1;
1908 
1909   assert (state_data);
1910   assert (exception_actions);
1911   assert (power_limit_requested);
1912   assert (correction_time_limit);
1913   assert (management_application_statistics_sampling_period);
1914   assert (errorbuf);
1915   assert (errorbuflen);
1916 
1917   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_get_power_limit_rs)))
1918     {
1919       snprintf (errorbuf,
1920                 errorbuflen,
1921                 "fiid_obj_create: %s",
1922                 strerror (errno));
1923       goto cleanup;
1924     }
1925 
1926   /* IPMI Workaround/Interpretation
1927    *
1928    * The DCMI spec indicates a potential completion code for the "Get
1929    * Power Limit" command as "No Set Power Limit" (0x80).  FreeIPMI
1930    * originally interpreted this to mean the "Set Power Limit" command
1931    * was not available.  Atleast one vendor interpreted this to mean
1932    * "No Power Limit Set".  One can consider this an English
1933    * interpretation issue of 'No set POWER LIMIT' vs. 'No SET POWER
1934    * LIMIT' (i.e. is "set" a verb or part of a proper noun referencing
1935    * the DCMI command).  Confounding this issue is the fact that the
1936    * example implementation in Intel's DCMItool implements the former,
1937    * while the DCMI Conformance test suite implements the latter.  In
1938    * addition to this, with the latter interpretation, it need not be
1939    * an indication of an error, but rather a flag.  So the rest of the
1940    * packet can be completely full of legitimate data.
1941    *
1942    * So how do we handle this?
1943    *
1944    * If we hit "No Set Power Limit", try to read data.  If we can't
1945    * read data (b/c it's not set), fail out, but preserve the "No Set
1946    * Power Limit" error message.
1947    */
1948 
1949   if (ipmi_cmd_dcmi_get_power_limit (state_data->ipmi_ctx,
1950                                      obj_cmd_rs) < 0)
1951     {
1952       if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
1953           && comp_code)
1954         {
1955           (*comp_code) = 0;
1956           if (FIID_OBJ_GET (obj_cmd_rs, "comp_code", &val) < 0)
1957             {
1958               snprintf (errorbuf,
1959                         errorbuflen,
1960                         "fiid_obj_get: 'comp_code': %s",
1961                         fiid_obj_errormsg (obj_cmd_rs));
1962               goto cleanup;
1963             }
1964           (*comp_code) = val;
1965         }
1966 
1967       if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
1968           && ipmi_check_completion_code (obj_cmd_rs,
1969                                          IPMI_COMP_CODE_DCMI_NO_SET_POWER_LIMIT) == 1)
1970         {
1971           snprintf (errorbuf,
1972                     errorbuflen,
1973                     "ipmi_cmd_dcmi_get_power_limit: %s",
1974                     IPMI_COMP_CODE_DCMI_NO_SET_POWER_LIMIT_STR);
1975           no_set_power_limit_error_flag++;
1976           goto read_data;
1977         }
1978       else
1979         snprintf (errorbuf,
1980                   errorbuflen,
1981                   "ipmi_cmd_dcmi_get_power_limit: %s",
1982                   ipmi_ctx_errormsg (state_data->ipmi_ctx));
1983 
1984       goto cleanup;
1985     }
1986 
1987  read_data:
1988 
1989   if (FIID_OBJ_GET (obj_cmd_rs,
1990                     "exception_actions",
1991                     &val) < 0)
1992     {
1993       if (!no_set_power_limit_error_flag
1994           || fiid_obj_errnum (obj_cmd_rs) != FIID_ERR_DATA_NOT_AVAILABLE)
1995         snprintf (errorbuf,
1996                   errorbuflen,
1997                   "fiid_obj_get: 'exception_actions': %s",
1998                   fiid_obj_errormsg (obj_cmd_rs));
1999       goto cleanup;
2000     }
2001   (*exception_actions) = val;
2002 
2003   if (FIID_OBJ_GET (obj_cmd_rs,
2004                     "power_limit_requested",
2005                     &val) < 0)
2006     {
2007       if (!no_set_power_limit_error_flag
2008           || fiid_obj_errnum (obj_cmd_rs) != FIID_ERR_DATA_NOT_AVAILABLE)
2009         snprintf (errorbuf,
2010                   errorbuflen,
2011                   "fiid_obj_get: 'power_limit_requested': %s",
2012                   fiid_obj_errormsg (obj_cmd_rs));
2013       goto cleanup;
2014     }
2015   (*power_limit_requested) = val;
2016 
2017   if (FIID_OBJ_GET (obj_cmd_rs,
2018                     "correction_time_limit",
2019                     &val) < 0)
2020     {
2021       if (!no_set_power_limit_error_flag
2022           || fiid_obj_errnum (obj_cmd_rs) != FIID_ERR_DATA_NOT_AVAILABLE)
2023         snprintf (errorbuf,
2024                   errorbuflen,
2025                   "fiid_obj_get: 'correction_time_limit': %s",
2026                   fiid_obj_errormsg (obj_cmd_rs));
2027       goto cleanup;
2028     }
2029   (*correction_time_limit) = val;
2030 
2031   if (FIID_OBJ_GET (obj_cmd_rs,
2032                     "management_application_statistics_sampling_period",
2033                     &val) < 0)
2034     {
2035       if (!no_set_power_limit_error_flag
2036           || fiid_obj_errnum (obj_cmd_rs) != FIID_ERR_DATA_NOT_AVAILABLE)
2037         snprintf (errorbuf,
2038                   errorbuflen,
2039                   "fiid_obj_get: 'management_application_statistics_sampling_period': %s",
2040                   fiid_obj_errormsg (obj_cmd_rs));
2041       goto cleanup;
2042     }
2043   (*management_application_statistics_sampling_period) = val;
2044 
2045   rv = 0;
2046  cleanup:
2047   fiid_obj_destroy (obj_cmd_rs);
2048   return (rv);
2049 }
2050 
2051 
2052 static int
2053 get_power_limit (ipmi_dcmi_state_data_t *state_data)
2054 {
2055   uint8_t exception_actions;
2056   uint16_t power_limit_requested;
2057   uint32_t correction_time_limit;
2058   uint16_t management_application_statistics_sampling_period;
2059   char errorbuf[IPMI_DCMI_ERROR_BUFLEN + 1];
2060 
2061   assert (state_data);
2062 
2063   memset (errorbuf, '\0', IPMI_DCMI_ERROR_BUFLEN + 1);
2064 
2065   if (_get_power_limit (state_data,
2066                         &exception_actions,
2067                         &power_limit_requested,
2068                         &correction_time_limit,
2069                         &management_application_statistics_sampling_period,
2070                         NULL,   /* comp_code */
2071                         errorbuf,
2072                         IPMI_DCMI_ERROR_BUFLEN) < 0)
2073     {
2074       pstdout_fprintf (state_data->pstate,
2075                        stderr,
2076                        "%s\n",
2077                        errorbuf);
2078       return (-1);
2079     }
2080 
2081   /* XXX: figure out OEM specifics, and list details given manufacturer ID/product ID */
2082   /*
2083 
2084   From Holger Liebig at Fujitsu
2085 
2086   "Regarding OEM exception action we currently do support 'continue'
2087   (0x02) and 'shutdown' (0x03) in addition to the 'hard power off'."
2088 
2089   But I don't know what the manufacturer ID and product ID are, so we leave it out for now.
2090 
2091   */
2092 
2093   if (exception_actions == IPMI_DCMI_EXCEPTION_ACTION_NO_ACTION)
2094     pstdout_printf (state_data->pstate,
2095                     "Exception Actions                                 : No Action (%Xh)\n",
2096                     exception_actions);
2097   else if (exception_actions == IPMI_DCMI_EXCEPTION_ACTION_HARD_POWER_OFF_SYSTEM)
2098     pstdout_printf (state_data->pstate,
2099                     "Exception Actions                                 : Hard Power Off system (%Xh)\n",
2100                     exception_actions);
2101   else if (exception_actions == IPMI_DCMI_EXCEPTION_ACTION_LOG_EVENT_TO_SEL_ONLY)
2102     pstdout_printf (state_data->pstate,
2103                     "Exception Actions                                 : Log event to SEL only (%Xh)\n",
2104                     exception_actions);
2105   else if ((exception_actions >= IPMI_DCMI_EXCEPTION_ACTION_OEM_MIN)
2106            && (exception_actions <= IPMI_DCMI_EXCEPTION_ACTION_OEM_MAX))
2107     pstdout_printf (state_data->pstate,
2108                     "Exception Actions                                 : OEM action (%Xh)\n",
2109                     exception_actions);
2110   else
2111     pstdout_printf (state_data->pstate,
2112                     "Exception Actions                                 : Unknown action (%Xh)\n",
2113                     exception_actions);
2114 
2115   pstdout_printf (state_data->pstate,
2116                   "Power Limit Requested                             : %u watts\n",
2117                   power_limit_requested);
2118 
2119   pstdout_printf (state_data->pstate,
2120                   "Correction time limit                             : %u milliseconds\n",
2121                   correction_time_limit);
2122 
2123   pstdout_printf (state_data->pstate,
2124                   "Management application Statistics Sampling period : %u seconds\n",
2125                   management_application_statistics_sampling_period);
2126 
2127   return (0);
2128 }
2129 
2130 static int
2131 set_power_limit (ipmi_dcmi_state_data_t *state_data)
2132 {
2133   struct ipmi_dcmi_arguments *args;
2134   fiid_obj_t obj_cmd_rs = NULL;
2135   uint8_t comp_code = 0;
2136   uint8_t exception_actions;
2137   uint16_t power_limit_requested;
2138   uint32_t correction_time_limit;
2139   uint16_t management_application_statistics_sampling_period;
2140   char errorbuf[IPMI_DCMI_ERROR_BUFLEN + 1];
2141   int rv = -1;
2142 
2143   assert (state_data);
2144   assert (state_data->prog_data->args->exception_actions
2145           || state_data->prog_data->args->power_limit_requested
2146           || state_data->prog_data->args->correction_time_limit
2147           || state_data->prog_data->args->statistics_sampling_period);
2148 
2149   args = state_data->prog_data->args;
2150 
2151   memset (errorbuf, '\0', IPMI_DCMI_ERROR_BUFLEN + 1);
2152 
2153   if (_get_power_limit (state_data,
2154                         &exception_actions,
2155                         &power_limit_requested,
2156                         &correction_time_limit,
2157                         &management_application_statistics_sampling_period,
2158                         &comp_code,
2159                         errorbuf,
2160                         IPMI_DCMI_ERROR_BUFLEN) < 0)
2161     {
2162       /* IPMI Workaround/Interpretation
2163        *
2164        * The DCMI spec indicates a potential completion code for the
2165        * "Get Power Limit" command as "No Set Power Limit" (0x80).
2166        * FreeIPMI originally interpreted this to mean the "Set Power
2167        * Limit" command was not available.  Atleast one vendor
2168        * interpreted this to mean "No Power Limit Set".  One can
2169        * consider this an English interpretation issue of 'No set
2170        * POWER LIMIT' vs. 'No SET POWER LIMIT' (i.e. is "set" a verb
2171        * or part of a proper noun referencing the DCMI command).
2172        * Confounding this issue is the fact that the example
2173        * implementation in Intel's DCMItool implements the former,
2174        * while the DCMI Conformance test suite implements the latter.
2175        * In addition to this, with the latter interpretation, it need
2176        * not be an indication of an error, but rather a flag.  So the
2177        * rest of the packet can be completely full of legitimate data.
2178        *
2179        * So we will do the following.
2180        *
2181        * If the "No Set Power Limit" completion code is returned and
2182        * we were able to read all of the fields, _get_power_limit() will
2183        * return normally and this error fallthrough won't occur.
2184        *
2185        * If the "No Set Power Limit", completion code is returned and
2186        * we were *not* able to read all of the fields, we won't have
2187        * values from "Get Power Limit" and won't know how to do the
2188        * configuration properly in "Set Power Limit".  So we will
2189        * require that the user input all fields for "Set Power Limit".
2190        */
2191       if (comp_code == IPMI_COMP_CODE_DCMI_NO_SET_POWER_LIMIT)
2192         {
2193           if (!args->exception_actions
2194               || !args->power_limit_requested
2195               || !args->correction_time_limit
2196               || !args->statistics_sampling_period)
2197             {
2198               pstdout_fprintf (state_data->pstate,
2199                                stderr,
2200                                "Must specify --exception-actions, --power-limit-requested, "
2201                                "--correction-time-limit, and --statistics-sampling-period\n");
2202               goto cleanup;
2203             }
2204         }
2205       else
2206         {
2207           pstdout_fprintf (state_data->pstate,
2208                            stderr,
2209                            "%s\n",
2210                            errorbuf);
2211           goto cleanup;
2212         }
2213     }
2214 
2215   if (!args->exception_actions)
2216     args->exception_actions_arg = exception_actions;
2217 
2218   if (!args->power_limit_requested)
2219     args->power_limit_requested_arg = power_limit_requested;
2220 
2221   if (!args->correction_time_limit)
2222     args->correction_time_limit_arg = correction_time_limit;
2223 
2224   if (!args->statistics_sampling_period)
2225     args->statistics_sampling_period_arg = management_application_statistics_sampling_period;
2226 
2227   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_set_power_limit_rs)))
2228     {
2229       pstdout_fprintf (state_data->pstate,
2230                        stderr,
2231                        "fiid_obj_create: %s\n",
2232                        strerror (errno));
2233       goto cleanup;
2234     }
2235 
2236   if (ipmi_cmd_dcmi_set_power_limit (state_data->ipmi_ctx,
2237                                      args->exception_actions_arg,
2238                                      args->power_limit_requested_arg,
2239                                      args->correction_time_limit_arg,
2240                                      args->statistics_sampling_period_arg,
2241                                      obj_cmd_rs) < 0)
2242     {
2243       if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
2244           && ipmi_check_completion_code (obj_cmd_rs,
2245                                          IPMI_COMP_CODE_DCMI_POWER_LIMIT_OUT_OF_RANGE) == 1)
2246         pstdout_fprintf (state_data->pstate,
2247                          stderr,
2248                          "ipmi_cmd_dcmi_set_power_limit: %s\n",
2249                          IPMI_COMP_CODE_DCMI_POWER_LIMIT_OUT_OF_RANGE_STR);
2250       else if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
2251                && ipmi_check_completion_code (obj_cmd_rs,
2252                                               IPMI_COMP_CODE_DCMI_CORRECTION_TIME_OUT_OF_RANGE) == 1)
2253         pstdout_fprintf (state_data->pstate,
2254                          stderr,
2255                          "ipmi_cmd_dcmi_set_power_limit: %s\n",
2256                          IPMI_COMP_CODE_DCMI_CORRECTION_TIME_OUT_OF_RANGE_STR);
2257       else if (ipmi_ctx_errnum (state_data->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE
2258                && ipmi_check_completion_code (obj_cmd_rs,
2259                                               IPMI_COMP_CODE_DCMI_STATISTICS_REPORTING_PERIOD_OUT_OF_RANGE) == 1)
2260         pstdout_fprintf (state_data->pstate,
2261                          stderr,
2262                          "ipmi_cmd_dcmi_set_power_limit: %s\n",
2263                          IPMI_COMP_CODE_DCMI_STATISTICS_REPORTING_PERIOD_OUT_OF_RANGE_STR);
2264       else
2265         pstdout_fprintf (state_data->pstate,
2266                          stderr,
2267                          "ipmi_cmd_dcmi_set_power_limit: %s\n",
2268                          ipmi_ctx_errormsg (state_data->ipmi_ctx));
2269       goto cleanup;
2270     }
2271 
2272   rv = 0;
2273  cleanup:
2274   fiid_obj_destroy (obj_cmd_rs);
2275   return (rv);
2276 }
2277 
2278 static int
2279 activate_deactivate_power_limit (ipmi_dcmi_state_data_t *state_data)
2280 {
2281   struct ipmi_dcmi_arguments *args;
2282   fiid_obj_t obj_cmd_rs = NULL;
2283   int rv = -1;
2284 
2285   assert (state_data);
2286 
2287   args = state_data->prog_data->args;
2288 
2289   if (!(obj_cmd_rs = fiid_obj_create (tmpl_cmd_dcmi_activate_deactivate_power_limit_rs)))
2290     {
2291       pstdout_fprintf (state_data->pstate,
2292                        stderr,
2293                        "fiid_obj_create: %s\n",
2294                        strerror (errno));
2295       goto cleanup;
2296     }
2297 
2298   if (ipmi_cmd_dcmi_activate_deactivate_power_limit (state_data->ipmi_ctx,
2299                                                      args->activate_deactivate_power_limit_arg,
2300                                                      obj_cmd_rs) < 0)
2301     {
2302       pstdout_fprintf (state_data->pstate,
2303                        stderr,
2304                        "ipmi_cmd_dcmi_activate_deactivate_power_limit: %s\n",
2305                        ipmi_ctx_errormsg (state_data->ipmi_ctx));
2306       goto cleanup;
2307     }
2308 
2309   rv = 0;
2310  cleanup:
2311   fiid_obj_destroy (obj_cmd_rs);
2312   return (rv);
2313 }
2314 
2315 static int
2316 run_cmd_args (ipmi_dcmi_state_data_t *state_data)
2317 {
2318   struct ipmi_dcmi_arguments *args;
2319   int rv = -1;
2320 
2321   assert (state_data);
2322 
2323   args = state_data->prog_data->args;
2324 
2325   if (args->get_dcmi_capability_info)
2326     return (get_dcmi_capability_info (state_data));
2327 
2328   if (args->get_system_power_statistics)
2329     return (get_system_power_statistics (state_data));
2330 
2331   if (args->get_enhanced_system_power_statistics)
2332     return (get_enhanced_system_power_statistics (state_data));
2333 
2334   if (args->get_asset_tag)
2335     return (get_asset_tag (state_data));
2336 
2337   if (args->set_asset_tag)
2338     return (set_asset_tag (state_data));
2339 
2340   if (args->get_management_controller_identifier_string)
2341     return (get_management_controller_identifier_string (state_data));
2342 
2343   if (args->set_management_controller_identifier_string)
2344     return (set_management_controller_identifier_string (state_data));
2345 
2346   if (args->get_dcmi_sensor_info)
2347     return (get_dcmi_sensor_info (state_data));
2348 
2349   if (args->get_power_limit)
2350     return (get_power_limit (state_data));
2351 
2352   if (args->set_power_limit)
2353     return (set_power_limit (state_data));
2354 
2355   if (args->activate_deactivate_power_limit)
2356     return (activate_deactivate_power_limit (state_data));
2357 
2358   rv = 0;
2359   return (rv);
2360 }
2361 
2362 static int
2363 _ipmi_dcmi (pstdout_state_t pstate,
2364             const char *hostname,
2365             void *arg)
2366 {
2367   ipmi_dcmi_state_data_t state_data;
2368   ipmi_dcmi_prog_data_t *prog_data;
2369   int exit_code = EXIT_FAILURE;
2370 
2371   assert (pstate);
2372   assert (arg);
2373 
2374   prog_data = (ipmi_dcmi_prog_data_t *)arg;
2375   memset (&state_data, '\0', sizeof (ipmi_dcmi_state_data_t));
2376 
2377   state_data.prog_data = prog_data;
2378   state_data.pstate = pstate;
2379 
2380   if (!(state_data.ipmi_ctx = ipmi_open (prog_data->progname,
2381                                          hostname,
2382                                          &(prog_data->args->common_args),
2383                                          state_data.pstate,
2384                                          0)))
2385     goto cleanup;
2386 
2387   if (run_cmd_args (&state_data) < 0)
2388     goto cleanup;
2389 
2390   exit_code = EXIT_SUCCESS;
2391  cleanup:
2392   ipmi_ctx_close (state_data.ipmi_ctx);
2393   ipmi_ctx_destroy (state_data.ipmi_ctx);
2394   return (exit_code);
2395 }
2396 
2397 int
2398 main (int argc, char **argv)
2399 {
2400   ipmi_dcmi_prog_data_t prog_data;
2401   struct ipmi_dcmi_arguments cmd_args;
2402   int hosts_count;
2403   int rv;
2404 
2405   ipmi_disable_coredump ();
2406 
2407   prog_data.progname = argv[0];
2408   ipmi_dcmi_argp_parse (argc, argv, &cmd_args);
2409   prog_data.args = &cmd_args;
2410 
2411   if ((hosts_count = pstdout_setup (&(prog_data.args->common_args.hostname),
2412                                     &(prog_data.args->common_args))) < 0)
2413     return (EXIT_FAILURE);
2414 
2415   if (!hosts_count)
2416     return (EXIT_SUCCESS);
2417 
2418   if ((rv = pstdout_launch (prog_data.args->common_args.hostname,
2419                             _ipmi_dcmi,
2420                             &prog_data)) < 0)
2421     {
2422       fprintf (stderr,
2423                "pstdout_launch: %s\n",
2424                pstdout_strerror (pstdout_errnum));
2425       return (EXIT_FAILURE);
2426     }
2427 
2428   return (rv);
2429 }
2430