1 /*****************************************************************************\
2  *  $Id: ipmi-sdr-cache-read.c,v 1.33 2010-02-08 22:09:40 chu11 Exp $
3  *****************************************************************************
4  *  Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC.
5  *  Copyright (C) 2006-2007 The Regents of the University of California.
6  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7  *  Written by Albert Chu <chu11@llnl.gov>
8  *  UCRL-CODE-222073
9  *
10  *  This file is part of Ipmimonitoring, an IPMI sensor monitoring
11  *  library.  For details, see http://www.llnl.gov/linux/.
12  *
13  *  Ipmimonitoring 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  *  Ipmimonitoring 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 Ipmimonitoring.  If not, see <http://www.gnu.org/licenses/>.
25 \*****************************************************************************/
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* HAVE_CONFIG_H */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #if STDC_HEADERS
34 #include <string.h>
35 #endif /* STDC_HEADERS */
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #if HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif /* HAVE_FCNTL_H */
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif /* HAVE_UNISTD_H */
44 #include <sys/param.h>
45 #include <sys/mman.h>
46 #include <assert.h>
47 #include <errno.h>
48 
49 #include "freeipmi/sdr/ipmi-sdr.h"
50 #include "freeipmi/record-format/ipmi-sdr-record-format.h"
51 #include "freeipmi/util/ipmi-util.h"
52 
53 #include "ipmi-sdr-common.h"
54 #include "ipmi-sdr-defs.h"
55 #include "ipmi-sdr-trace.h"
56 #include "ipmi-sdr-util.h"
57 
58 #include "freeipmi-portability.h"
59 
60 static void
_sdr_set_current_offset(ipmi_sdr_ctx_t ctx,off_t new_offset)61 _sdr_set_current_offset (ipmi_sdr_ctx_t ctx, off_t new_offset)
62 {
63   assert (ctx);
64   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
65 
66   ctx->current_offset.offset = new_offset;
67   ctx->current_offset.offset_dumped = 0;
68 }
69 
70 int
ipmi_sdr_cache_open(ipmi_sdr_ctx_t ctx,ipmi_ctx_t ipmi_ctx,const char * filename)71 ipmi_sdr_cache_open (ipmi_sdr_ctx_t ctx,
72                      ipmi_ctx_t ipmi_ctx,
73                      const char *filename)
74 {
75   uint8_t sdr_version;
76   uint16_t record_count;
77   uint32_t most_recent_addition_timestamp, most_recent_erase_timestamp;
78   char sdr_version_buf;
79   char sdr_cache_magic_buf[4];
80   char sdr_cache_version_buf[4];
81   char record_count_buf[2];
82   char most_recent_addition_timestamp_buf[4];
83   char most_recent_erase_timestamp_buf[4];
84   struct stat stat_buf;
85 
86   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
87     {
88       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
89       return (-1);
90     }
91 
92   if (!filename)
93     {
94       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
95       return (-1);
96     }
97 
98   if (ctx->operation != IPMI_SDR_OPERATION_UNINITIALIZED)
99     {
100       if (ctx->operation == IPMI_SDR_OPERATION_READ_CACHE)
101         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_ALREADY_INITIALIZED);
102       else
103         SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
104       return (-1);
105     }
106 
107   if (stat (filename, &stat_buf) < 0)
108     {
109       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
110       goto cleanup;
111     }
112 
113   /* File Size must be atleast magic_buf + file_version_buf +
114    * sdr_version_buf + record_count_buf +
115    * most_recent_addition_timestamp_buf +
116    * most_recent_erase_timestamp-buf in size.
117    */
118 
119   ctx->file_size = stat_buf.st_size;
120   if (ctx->file_size < (4 + 4 + 1 + 2 + 4 + 4))
121     {
122       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
123       goto cleanup;
124     }
125 
126   if ((ctx->fd = open (filename, O_RDONLY)) < 0)
127     {
128       SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
129       goto cleanup;
130     }
131 
132   ctx->sdr_cache = (uint8_t *)mmap (NULL,
133                                     ctx->file_size,
134                                     PROT_READ,
135                                     MAP_PRIVATE,
136                                     ctx->fd,
137                                     0);
138   if (!ctx->sdr_cache || ctx->sdr_cache == ((void *) -1))
139     {
140       ERRNO_TRACE (errno);
141       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
142       goto cleanup;
143     }
144 
145   memcpy (sdr_cache_magic_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
146   ctx->records_start_offset += 4;
147   memcpy (sdr_cache_version_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
148   ctx->records_start_offset += 4;
149   memcpy (&sdr_version_buf, ctx->sdr_cache + ctx->records_start_offset, 1);
150   ctx->records_start_offset += 1;
151   memcpy (record_count_buf, ctx->sdr_cache + ctx->records_start_offset, 2);
152   ctx->records_start_offset += 2;
153   memcpy (most_recent_addition_timestamp_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
154   ctx->records_start_offset += 4;
155   memcpy (most_recent_erase_timestamp_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
156   ctx->records_start_offset += 4;
157 
158   if ((uint8_t)sdr_cache_magic_buf[0] != IPMI_SDR_CACHE_FILE_MAGIC_0
159       || (uint8_t)sdr_cache_magic_buf[1] != IPMI_SDR_CACHE_FILE_MAGIC_1
160       || (uint8_t)sdr_cache_magic_buf[2] != IPMI_SDR_CACHE_FILE_MAGIC_2
161       || (uint8_t)sdr_cache_magic_buf[3] != IPMI_SDR_CACHE_FILE_MAGIC_3)
162     {
163       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
164       goto cleanup;
165     }
166 
167   if (((uint8_t)sdr_cache_version_buf[0] != IPMI_SDR_CACHE_FILE_VERSION_1_0
168        || (uint8_t)sdr_cache_version_buf[1] != IPMI_SDR_CACHE_FILE_VERSION_1_1
169        || (uint8_t)sdr_cache_version_buf[2] != IPMI_SDR_CACHE_FILE_VERSION_1_2
170        || (uint8_t)sdr_cache_version_buf[3] != IPMI_SDR_CACHE_FILE_VERSION_1_3)
171       && ((uint8_t)sdr_cache_version_buf[0] != IPMI_SDR_CACHE_FILE_VERSION_1_2_0
172           || (uint8_t)sdr_cache_version_buf[1] != IPMI_SDR_CACHE_FILE_VERSION_1_2_1
173           || (uint8_t)sdr_cache_version_buf[2] != IPMI_SDR_CACHE_FILE_VERSION_1_2_2
174           || (uint8_t)sdr_cache_version_buf[3] != IPMI_SDR_CACHE_FILE_VERSION_1_2_3))
175     {
176       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
177       goto cleanup;
178     }
179 
180   ctx->sdr_version = (uint8_t)sdr_version_buf;
181   ctx->record_count = ((uint16_t)record_count_buf[0] & 0xFF);
182   ctx->record_count |= ((uint16_t)record_count_buf[1] & 0xFF) << 8;
183   ctx->most_recent_addition_timestamp = ((uint32_t)most_recent_addition_timestamp_buf[0] & 0xFF);
184   ctx->most_recent_addition_timestamp |= ((uint32_t)most_recent_addition_timestamp_buf[1] & 0xFF) << 8;
185   ctx->most_recent_addition_timestamp |= ((uint32_t)most_recent_addition_timestamp_buf[2] & 0xFF) << 16;
186   ctx->most_recent_addition_timestamp |= ((uint32_t)most_recent_addition_timestamp_buf[3] & 0xFF) << 24;
187   ctx->most_recent_erase_timestamp = ((uint32_t)most_recent_erase_timestamp_buf[0] & 0xFF);
188   ctx->most_recent_erase_timestamp |= ((uint32_t)most_recent_erase_timestamp_buf[1] & 0xFF) << 8;
189   ctx->most_recent_erase_timestamp |= ((uint32_t)most_recent_erase_timestamp_buf[2] & 0xFF) << 16;
190   ctx->most_recent_erase_timestamp |= ((uint32_t)most_recent_erase_timestamp_buf[3] & 0xFF) << 24;
191 
192   if (ipmi_ctx)
193     {
194       if (sdr_info (ctx,
195                     ipmi_ctx,
196                     &sdr_version,
197                     &record_count,
198                     &most_recent_addition_timestamp,
199                     &most_recent_erase_timestamp) < 0)
200         goto cleanup;
201 
202       if (ctx->sdr_version != sdr_version
203           || ctx->most_recent_addition_timestamp != most_recent_addition_timestamp
204           || ctx->most_recent_erase_timestamp != most_recent_erase_timestamp)
205         {
206           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_OUT_OF_DATE);
207           goto cleanup;
208         }
209     }
210 
211   if ((uint8_t)sdr_cache_version_buf[0] == IPMI_SDR_CACHE_FILE_VERSION_1_2_0
212       && (uint8_t)sdr_cache_version_buf[1] == IPMI_SDR_CACHE_FILE_VERSION_1_2_1
213       && (uint8_t)sdr_cache_version_buf[2] == IPMI_SDR_CACHE_FILE_VERSION_1_2_2
214       && (uint8_t)sdr_cache_version_buf[3] == IPMI_SDR_CACHE_FILE_VERSION_1_2_3)
215     {
216       uint8_t header_checksum_buf[512];
217       unsigned int header_checksum_buf_len = 0;
218       uint8_t header_checksum, header_checksum_cache;
219       uint8_t trailer_checksum, trailer_checksum_cache;
220       char total_bytes_written_buf[4];
221       unsigned int total_bytes_written;
222       unsigned int header_bytes_len;
223       unsigned int trailer_bytes_len;
224 
225       /* File Size must be atleast magic_buf + file_version_buf +
226        * sdr_version_buf + record_count_buf +
227        * most_recent_addition_timestamp_buf +
228        * most_recent_erase_timestamp-buf + header_checksum + trailer
229        * bytes written + trailer records checksum.
230        */
231 
232       header_bytes_len = 4 + 4 + 1 + 2 + 4 + 4 + 1;
233       trailer_bytes_len = 4 + 1;
234 
235       if (ctx->file_size < (header_bytes_len + trailer_bytes_len))
236         {
237           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
238           goto cleanup;
239         }
240 
241       memcpy (&header_checksum_cache, ctx->sdr_cache + ctx->records_start_offset, 1);
242       ctx->records_start_offset += 1;
243 
244       memcpy(&header_checksum_buf[header_checksum_buf_len], sdr_cache_magic_buf, 4);
245       header_checksum_buf_len += 4;
246       memcpy(&header_checksum_buf[header_checksum_buf_len], sdr_cache_version_buf, 4);
247       header_checksum_buf_len += 4;
248       memcpy(&header_checksum_buf[header_checksum_buf_len], &sdr_version_buf, 1);
249       header_checksum_buf_len += 1;
250       memcpy(&header_checksum_buf[header_checksum_buf_len], record_count_buf, 2);
251       header_checksum_buf_len += 2;
252       memcpy(&header_checksum_buf[header_checksum_buf_len], most_recent_addition_timestamp_buf, 4);
253       header_checksum_buf_len += 4;
254       memcpy(&header_checksum_buf[header_checksum_buf_len], most_recent_erase_timestamp_buf, 4);
255       header_checksum_buf_len += 4;
256 
257       header_checksum = ipmi_checksum (header_checksum_buf, header_checksum_buf_len);
258       if (header_checksum != header_checksum_cache)
259         {
260           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
261           goto cleanup;
262         }
263 
264       /* total_bytes_written is written before checksum */
265       /* checksum of records is last byte written */
266       memcpy (total_bytes_written_buf, ctx->sdr_cache + ctx->file_size - 5, 4);
267       memcpy (&trailer_checksum_cache, ctx->sdr_cache + ctx->file_size - 1, 1);
268 
269       total_bytes_written = ((uint32_t)total_bytes_written_buf[0] & 0xFF);
270       total_bytes_written |= ((uint32_t)total_bytes_written_buf[1] & 0xFF) << 8;
271       total_bytes_written |= ((uint32_t)total_bytes_written_buf[2] & 0xFF) << 16;
272       total_bytes_written |= ((uint32_t)total_bytes_written_buf[3] & 0xFF) << 24;
273 
274       if (total_bytes_written != ctx->file_size)
275         {
276           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
277           goto cleanup;
278         }
279 
280       /* -1 for checksum */
281       trailer_checksum = ipmi_checksum (ctx->sdr_cache + ctx->records_start_offset,
282                                         total_bytes_written - ctx->records_start_offset - 1);
283 
284       if (trailer_checksum != trailer_checksum_cache)
285         {
286           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
287           goto cleanup;
288         }
289 
290       ctx->records_end_offset = ctx->file_size - trailer_bytes_len;
291     }
292   else /* (uint8_t)sdr_cache_version_buf[0] == IPMI_SDR_CACHE_FILE_VERSION_1_0
293           && (uint8_t)sdr_cache_version_buf[1] == IPMI_SDR_CACHE_FILE_VERSION_1_1
294           && (uint8_t)sdr_cache_version_buf[2] == IPMI_SDR_CACHE_FILE_VERSION_1_2
295           && (uint8_t)sdr_cache_version_buf[3] == IPMI_SDR_CACHE_FILE_VERSION_1_3 */
296     ctx->records_end_offset = ctx->file_size;
297 
298   _sdr_set_current_offset (ctx, ctx->records_start_offset);
299   ctx->operation = IPMI_SDR_OPERATION_READ_CACHE;
300   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
301   return (0);
302 
303  cleanup:
304   /* ignore potential error, cleanup path */
305   if (ctx->fd >= 0)
306     close (ctx->fd);
307   /* ignore potential error, cleanup path */
308   if (ctx->sdr_cache)
309     munmap ((void *)ctx->sdr_cache, ctx->file_size);
310   sdr_init_ctx (ctx);
311   return (-1);
312 }
313 
314 int
ipmi_sdr_cache_sdr_version(ipmi_sdr_ctx_t ctx,uint8_t * sdr_version)315 ipmi_sdr_cache_sdr_version (ipmi_sdr_ctx_t ctx, uint8_t *sdr_version)
316 {
317   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
318     {
319       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
320       return (-1);
321     }
322 
323   if (!sdr_version)
324     {
325       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
326       return (-1);
327     }
328 
329   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
330     {
331       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
332       return (-1);
333     }
334 
335   *sdr_version = ctx->sdr_version;
336   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
337   return (0);
338 }
339 
340 int
ipmi_sdr_cache_record_count(ipmi_sdr_ctx_t ctx,uint16_t * record_count)341 ipmi_sdr_cache_record_count (ipmi_sdr_ctx_t ctx, uint16_t *record_count)
342 {
343   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
344     {
345       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
346       return (-1);
347     }
348 
349   if (!record_count)
350     {
351       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
352       return (-1);
353     }
354 
355   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
356     {
357       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
358       return (-1);
359     }
360 
361   *record_count = ctx->record_count;
362   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
363   return (0);
364 }
365 
366 int
ipmi_sdr_cache_most_recent_addition_timestamp(ipmi_sdr_ctx_t ctx,uint32_t * most_recent_addition_timestamp)367 ipmi_sdr_cache_most_recent_addition_timestamp (ipmi_sdr_ctx_t ctx, uint32_t *most_recent_addition_timestamp)
368 {
369   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
370     {
371       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
372       return (-1);
373     }
374 
375   if (!most_recent_addition_timestamp)
376     {
377       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
378       return (-1);
379     }
380 
381   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
382     {
383       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
384       return (-1);
385     }
386 
387   *most_recent_addition_timestamp = ctx->most_recent_addition_timestamp;
388   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
389   return (0);
390 }
391 
392 int
ipmi_sdr_cache_most_recent_erase_timestamp(ipmi_sdr_ctx_t ctx,uint32_t * most_recent_erase_timestamp)393 ipmi_sdr_cache_most_recent_erase_timestamp (ipmi_sdr_ctx_t ctx, uint32_t *most_recent_erase_timestamp)
394 {
395   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
396     {
397       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
398       return (-1);
399     }
400 
401   if (!most_recent_erase_timestamp)
402     {
403       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
404       return (-1);
405     }
406 
407   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
408     {
409       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
410       return (-1);
411     }
412 
413   *most_recent_erase_timestamp = ctx->most_recent_erase_timestamp;
414   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
415   return (0);
416 }
417 
418 int
ipmi_sdr_cache_first(ipmi_sdr_ctx_t ctx)419 ipmi_sdr_cache_first (ipmi_sdr_ctx_t ctx)
420 {
421   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
422     {
423       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
424       return (-1);
425     }
426 
427   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
428     {
429       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
430       return (-1);
431     }
432 
433   _sdr_set_current_offset (ctx, ctx->records_start_offset);
434 
435   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
436   return (0);
437 }
438 
439 int
ipmi_sdr_cache_next(ipmi_sdr_ctx_t ctx)440 ipmi_sdr_cache_next (ipmi_sdr_ctx_t ctx)
441 {
442   unsigned int record_length;
443 
444   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
445     {
446       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
447       return (-1);
448     }
449 
450   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
451     {
452       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
453       return (-1);
454     }
455 
456   record_length = (uint8_t)((ctx->sdr_cache + ctx->current_offset.offset)[IPMI_SDR_RECORD_LENGTH_INDEX]);
457 
458   if ((ctx->current_offset.offset + record_length + IPMI_SDR_RECORD_HEADER_LENGTH) >= ctx->records_end_offset)
459     return (0);
460 
461   _sdr_set_current_offset (ctx, ctx->current_offset.offset + IPMI_SDR_RECORD_HEADER_LENGTH + record_length);
462 
463   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
464   return (1);
465 }
466 
467 int
ipmi_sdr_cache_seek(ipmi_sdr_ctx_t ctx,unsigned int index)468 ipmi_sdr_cache_seek (ipmi_sdr_ctx_t ctx, unsigned int index)
469 {
470   off_t offset;
471   unsigned int i;
472 
473   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
474     {
475       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
476       return (-1);
477     }
478 
479   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
480     {
481       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
482       return (-1);
483     }
484 
485   if (index >= ctx->record_count)
486     {
487       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
488       return (-1);
489     }
490 
491   offset = ctx->records_start_offset;
492   for (i = 0; i < index; i++)
493     {
494       unsigned int record_length;
495 
496       record_length = (uint8_t)((ctx->sdr_cache + offset)[IPMI_SDR_RECORD_LENGTH_INDEX]);
497 
498       if ((offset + record_length + IPMI_SDR_RECORD_HEADER_LENGTH) >= ctx->records_end_offset)
499         break;
500 
501       offset += IPMI_SDR_RECORD_HEADER_LENGTH;
502       offset += record_length;
503     }
504 
505   _sdr_set_current_offset (ctx, offset);
506 
507   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
508   return (0);
509 }
510 
511 int
ipmi_sdr_cache_search_record_id(ipmi_sdr_ctx_t ctx,uint16_t record_id)512 ipmi_sdr_cache_search_record_id (ipmi_sdr_ctx_t ctx, uint16_t record_id)
513 {
514   off_t offset;
515   int found = 0;
516 
517   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
518     {
519       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
520       return (-1);
521     }
522 
523   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
524     {
525       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
526       return (-1);
527     }
528 
529   offset = ctx->records_start_offset;
530   while (offset < ctx->records_end_offset)
531     {
532       uint8_t *ptr = ctx->sdr_cache + offset;
533       uint16_t record_id_current;
534       unsigned int record_length;
535 
536       /* Record ID stored little-endian */
537       record_id_current = (uint16_t)ptr[IPMI_SDR_RECORD_ID_INDEX_LS] & 0xFF;
538       record_id_current |= ((uint16_t)ptr[IPMI_SDR_RECORD_ID_INDEX_MS] & 0xFF) << 8;
539 
540       if (record_id == record_id_current)
541         {
542           found++;
543           _sdr_set_current_offset (ctx, offset);
544           break;
545         }
546 
547       record_length = (uint8_t)((ctx->sdr_cache + offset)[IPMI_SDR_RECORD_LENGTH_INDEX]);
548 
549       if ((offset + record_length + IPMI_SDR_RECORD_HEADER_LENGTH) >= ctx->records_end_offset)
550         break;
551 
552       offset += IPMI_SDR_RECORD_HEADER_LENGTH;
553       offset += record_length;
554     }
555 
556   if (!found)
557     {
558       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_NOT_FOUND);
559       return (-1);
560     }
561 
562   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
563   return (0);
564 }
565 
566 int
ipmi_sdr_cache_search_sensor(ipmi_sdr_ctx_t ctx,uint8_t sensor_number,uint8_t sensor_owner_id)567 ipmi_sdr_cache_search_sensor (ipmi_sdr_ctx_t ctx, uint8_t sensor_number, uint8_t sensor_owner_id)
568 {
569   off_t offset;
570   int found = 0;
571 
572   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
573     {
574       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
575       return (-1);
576     }
577 
578   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
579     {
580       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
581       return (-1);
582     }
583 
584   offset = ctx->records_start_offset;
585   while (offset < ctx->records_end_offset)
586     {
587       uint8_t *ptr = ctx->sdr_cache + offset;
588       uint8_t record_type_current;
589       unsigned int record_length;
590 
591       record_type_current = ptr[IPMI_SDR_RECORD_TYPE_INDEX];
592 
593       if (record_type_current == IPMI_SDR_FORMAT_FULL_SENSOR_RECORD
594           || record_type_current == IPMI_SDR_FORMAT_COMPACT_SENSOR_RECORD
595           || record_type_current == IPMI_SDR_FORMAT_EVENT_ONLY_RECORD)
596         {
597           uint8_t sensor_number_current;
598           uint8_t sensor_owner_id_current;
599 
600           sensor_owner_id_current = ptr[IPMI_SDR_RECORD_SENSOR_OWNER_ID_INDEX];
601           sensor_number_current = ptr[IPMI_SDR_RECORD_SENSOR_NUMBER_INDEX];
602 
603           if (sensor_owner_id_current == sensor_owner_id
604               && sensor_number_current == sensor_number)
605             {
606               found++;
607               _sdr_set_current_offset (ctx, offset);
608               break;
609             }
610 
611           /* Compact sensor records can do record sharing, so check
612            * for this case if the sensor_owner_id matches up.
613            */
614           if (sensor_owner_id_current == sensor_owner_id
615               && (record_type_current == IPMI_SDR_FORMAT_COMPACT_SENSOR_RECORD
616                   || record_type_current == IPMI_SDR_FORMAT_EVENT_ONLY_RECORD))
617             {
618               uint8_t share_count;
619 
620               if (record_type_current == IPMI_SDR_FORMAT_COMPACT_SENSOR_RECORD)
621                 {
622                   share_count = ptr[IPMI_SDR_RECORD_COMPACT_SHARE_COUNT];
623                   share_count &= IPMI_SDR_RECORD_COMPACT_SHARE_COUNT_BITMASK;
624                   share_count >>= IPMI_SDR_RECORD_COMPACT_SHARE_COUNT_SHIFT;
625                 }
626               else
627                 {
628                   share_count = ptr[IPMI_SDR_RECORD_EVENT_SHARE_COUNT];
629                   share_count &= IPMI_SDR_RECORD_EVENT_SHARE_COUNT_BITMASK;
630                   share_count >>= IPMI_SDR_RECORD_EVENT_SHARE_COUNT_SHIFT;
631                 }
632 
633               /* IPMI spec gives the following example:
634                *
635                * "If the starting sensor number was 10, and the share
636                * count was 3, then sensors 10, 11, and 12 would share
637                * the record"
638                */
639               if (share_count > 1
640                   && (sensor_number > sensor_number_current
641                       && sensor_number <= (sensor_number_current + (share_count - 1))))
642                 {
643                   found++;
644                   _sdr_set_current_offset (ctx, offset);
645                   break;
646                 }
647             }
648         }
649 
650       record_length = (uint8_t)((ctx->sdr_cache + offset)[IPMI_SDR_RECORD_LENGTH_INDEX]);
651 
652       if ((offset + record_length + IPMI_SDR_RECORD_HEADER_LENGTH) >= ctx->records_end_offset)
653         break;
654 
655       offset += IPMI_SDR_RECORD_HEADER_LENGTH;
656       offset += record_length;
657     }
658 
659   if (!found)
660     {
661       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_NOT_FOUND);
662       return (-1);
663     }
664 
665   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
666   return (0);
667 }
668 
669 int
ipmi_sdr_cache_record_read(ipmi_sdr_ctx_t ctx,void * buf,unsigned int buflen)670 ipmi_sdr_cache_record_read (ipmi_sdr_ctx_t ctx,
671                             void *buf,
672                             unsigned int buflen)
673 {
674   unsigned int record_length;
675 
676   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
677     {
678       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
679       return (-1);
680     }
681 
682   if (!buf || !buflen)
683     {
684       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
685       return (-1);
686     }
687 
688   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
689     {
690       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
691       return (-1);
692     }
693 
694   record_length = (uint8_t)((ctx->sdr_cache + ctx->current_offset.offset)[IPMI_SDR_RECORD_LENGTH_INDEX]);
695 
696   if (buflen < (record_length + IPMI_SDR_RECORD_HEADER_LENGTH))
697     {
698       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_OVERFLOW);
699       return (-1);
700     }
701 
702   sdr_check_read_status (ctx);
703 
704   memcpy (buf, ctx->sdr_cache + ctx->current_offset.offset, record_length + IPMI_SDR_RECORD_HEADER_LENGTH);
705   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
706   return (record_length + IPMI_SDR_RECORD_HEADER_LENGTH);
707 }
708 
709 static int
_sdr_save_current_offset(ipmi_sdr_ctx_t ctx)710 _sdr_save_current_offset (ipmi_sdr_ctx_t ctx)
711 {
712   struct ipmi_sdr_offset *saved_offset = NULL;
713 
714   assert (ctx);
715   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
716 
717   if (!(saved_offset = (struct ipmi_sdr_offset *)malloc (sizeof (struct ipmi_sdr_offset))))
718     {
719       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_OUT_OF_MEMORY);
720       return (-1);
721     }
722 
723   memcpy (saved_offset, &ctx->current_offset, sizeof (struct ipmi_sdr_offset));
724 
725   if (!list_push (ctx->saved_offsets, saved_offset))
726     {
727       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
728       goto cleanup;
729     }
730 
731   return (0);
732 
733  cleanup:
734   free (saved_offset);
735   return (-1);
736 }
737 
738 static int
_sdr_reset_current_offset(ipmi_sdr_ctx_t ctx)739 _sdr_reset_current_offset (ipmi_sdr_ctx_t ctx)
740 {
741   struct ipmi_sdr_offset *saved_offset = NULL;
742 
743   assert (ctx);
744   assert (ctx->magic == IPMI_SDR_CTX_MAGIC);
745   assert (list_count (ctx->saved_offsets) > 0);
746 
747   if (!(saved_offset = list_pop (ctx->saved_offsets)))
748     {
749       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
750       return (-1);
751     }
752 
753   memcpy (&ctx->current_offset, saved_offset, sizeof (struct ipmi_sdr_offset));
754   free (saved_offset);
755   return (0);
756 }
757 
758 int
ipmi_sdr_cache_iterate(ipmi_sdr_ctx_t ctx,Ipmi_Sdr_Cache_Iterate_Callback iterate_callback,void * iterate_callback_data)759 ipmi_sdr_cache_iterate (ipmi_sdr_ctx_t ctx,
760                         Ipmi_Sdr_Cache_Iterate_Callback iterate_callback,
761                         void *iterate_callback_data)
762 {
763   uint16_t record_count;
764   unsigned int i;
765   int tmpret;
766   int rv = -1;
767 
768   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
769     {
770       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
771       return (-1);
772     }
773 
774   if (!iterate_callback)
775     {
776       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
777       return (-1);
778     }
779 
780   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
781     {
782       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
783       return (-1);
784     }
785 
786   if (_sdr_save_current_offset (ctx) < 0)
787     return (-1);
788 
789   if (ipmi_sdr_cache_record_count (ctx, &record_count) < 0)
790     {
791       SDR_SET_INTERNAL_ERRNUM (ctx);
792       goto cleanup;
793     }
794 
795   if (ipmi_sdr_cache_first (ctx) < 0)
796     {
797       SDR_SET_INTERNAL_ERRNUM (ctx);
798       goto cleanup;
799     }
800 
801   for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (ctx))
802     {
803       uint8_t sdr_record[IPMI_SDR_MAX_RECORD_LENGTH];
804       int sdr_record_len;
805       uint8_t record_type;
806       int ret;
807 
808       memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH);
809 
810       if ((sdr_record_len = ipmi_sdr_cache_record_read (ctx,
811                                                         sdr_record,
812                                                         IPMI_SDR_MAX_RECORD_LENGTH)) < 0)
813         {
814           SDR_SET_INTERNAL_ERRNUM (ctx);
815           goto cleanup;
816         }
817 
818       record_type = sdr_record[IPMI_SDR_RECORD_TYPE_INDEX];
819 
820       ctx->callback_lock = 1;
821       ret = iterate_callback (ctx,
822                               record_type,
823                               sdr_record,
824                               (unsigned int)sdr_record_len,
825                               iterate_callback_data);
826       ctx->callback_lock = 0;
827 
828       if (ret < 0)
829         {
830           rv = ret;
831           SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_ERROR_RETURNED_IN_CALLBACK);
832           goto cleanup;
833         }
834 
835       if (ret > 0)
836         {
837           rv = ret;
838           goto out;
839         }
840     }
841 
842   rv = 0;
843  out:
844   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
845  cleanup:
846   tmpret = _sdr_reset_current_offset (ctx);
847   if (rv)
848     return (rv);
849   return (tmpret);
850 }
851 
852 int
ipmi_sdr_cache_close(ipmi_sdr_ctx_t ctx)853 ipmi_sdr_cache_close (ipmi_sdr_ctx_t ctx)
854 {
855   if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
856     {
857       ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
858       return (-1);
859     }
860 
861   if (ctx->operation != IPMI_SDR_OPERATION_READ_CACHE)
862     {
863       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_INITIALIZATION);
864       return (-1);
865     }
866 
867   if (ctx->callback_lock)
868     {
869       SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CONTEXT_PERFORMING_OTHER_OPERATION);
870       return (-1);
871     }
872 
873   /* ignore potential error, cleanup path */
874   if (ctx->fd >= 0)
875     close (ctx->fd);
876   /* ignore potential error, cleanup path */
877   if (ctx->sdr_cache)
878     munmap ((void *)ctx->sdr_cache, ctx->file_size);
879   sdr_init_ctx (ctx);
880 
881   ctx->operation = IPMI_SDR_OPERATION_UNINITIALIZED;
882   ctx->errnum = IPMI_SDR_ERR_SUCCESS;
883   return (0);
884 }
885