summaryrefslogtreecommitdiff
path: root/lib/ipmi_time.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ipmi_time.c')
-rw-r--r--lib/ipmi_time.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/lib/ipmi_time.c b/lib/ipmi_time.c
new file mode 100644
index 0000000..aecd656
--- /dev/null
+++ b/lib/ipmi_time.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2018 Alexander Amelkin. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of the copyright holder, nor the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * THE COPYRIGHT HOLDER AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * THE COPYRIGHT HOLDER OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE,
+ * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
+ * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE
+ * THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS
+ * SOFTWARE, EVEN IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGES.
+ */
+
+#include <time.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h> /* snprintf */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ipmitool/ipmi_time.h>
+
+bool time_in_utc; /* Set by '-Z' command line option */
+
+time_t
+ipmi_localtime2utc(time_t local)
+{
+ struct tm tm;
+ gmtime_r(&local, &tm);
+ tm.tm_isdst = (-1);
+ return mktime(&tm);
+}
+
+/**
+ * @brief Convert a timestamp to a formatted string,
+ * considering the '-Z' option. Acts as if tzset() was called.
+ *
+ * @param[out] s The output string buffer
+ * @param[in] max The size of the output string buffer including the
+ * terminating null byte
+ * @param[in] format The format string, as in strftime(), ignored for
+ * special timestamp values as per section 37.1 of
+ * IPMI v2.0 specification rev 1.1.
+ * @param[in] stamp The time stamp to convert
+ *
+ * @returns the number of bytes written to s or 0, see strftime()
+ */
+size_t
+ipmi_strftime(char *s, size_t max, const char *format, time_t stamp)
+{
+ struct tm tm;
+ /*
+ * There is a bug in gcc since 4.3.2 and still not fixed in 8.1.0.
+ * Even if __attribute__((format(strftime... is specified for a wrapper
+ * function around strftime, gcc still complains about strftime being
+ * called from the wrapper with a "non-literal" format argument.
+ *
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39438
+ *
+ * The following macro uses an "ugly cast" from that discussion to
+ * silence the compiler. The format string is checked for the wrapper
+ * because __attribute__((format)) is specified in the header file.
+ */
+ #define wrapstrftime(buf, buflen, fmt, t) \
+ ((size_t (*)(char *, size_t, const char *, const struct tm *))\
+ strftime)(buf, buflen, fmt, t)
+
+
+ if (IPMI_TIME_UNSPECIFIED == stamp) {
+ return snprintf(s, max, "Unknown");
+ }
+ else if (stamp <= IPMI_TIME_INIT_DONE) {
+ /* Timestamp is relative to BMC start, no GMT offset */
+ gmtime_r(&stamp, &tm);
+
+ return wrapstrftime(s, max, format, &tm);
+ }
+
+ if (time_in_utc || ipmi_timestamp_is_special(stamp)) {
+ /*
+ * The user wants the time reported in UTC or the stamp represents the
+ * number of seconds since system power on. In any case, don't apply
+ * the timezone offset.
+ */
+ gmtime_r(&stamp, &tm);
+ daylight = -1;
+ } else {
+ /*
+ * The user wants the time reported in local time zone.
+ */
+ localtime_r(&stamp, &tm);
+ }
+ return wrapstrftime(s, max, format, &tm);
+}
+
+/**
+ * @brief Convert a timestamp to string, considering the '-Z' option.
+ * Similar to asctime_r(), but takes time_t instead of struct tm,
+ * and the string is in form "Wed Jun 30 21:49:08 1993 TZD" without
+ * the new line at the end.
+ *
+ * @param[in] stamp The timestamp to convert
+ * @param[out] outbuf The buffer to write the string to.
+ * @param[in] len The maximum length of the output buffer.
+ * Recommended size is IPMI_ASCTIME_SZ.
+ *
+ * @returns outbuf
+ */
+char *
+ipmi_asctime_r(const time_t stamp, ipmi_datebuf_t outbuf)
+{
+ if (ipmi_timestamp_is_special(stamp)) {
+ if (stamp < SECONDS_A_DAY) {
+ ipmi_strftime(outbuf, IPMI_ASCTIME_SZ, "S+%H:%M:%S", stamp);
+ }
+ /*
+ * IPMI_TIME_INIT_DONE is over 17 years. This should never
+ * happen normally, but we'll support this anyway.
+ */
+ else {
+ ipmi_strftime(outbuf, IPMI_ASCTIME_SZ, "S+%yy %jd %H:%M:%S", stamp);
+ }
+ }
+
+ ipmi_strftime(outbuf, IPMI_ASCTIME_SZ, "%c %Z", stamp);
+ return outbuf;
+}
+
+char *
+ipmi_timestamp_fmt(uint32_t stamp, const char *fmt)
+{
+ /*
+ * It's assumed that supplied 'fmt' is never longer
+ * than IPMI_ASCTIME_SZ
+ */
+ static ipmi_datebuf_t datebuf;
+ /*
+ * There is a bug in gcc since 4.3.2 and still not fixed in 8.1.0.
+ * Even if __attribute__((format(strftime... is specified for a wrapper
+ * function around strftime, gcc still complains about strftime being
+ * called from the wrapper with a "non-literal" format argument.
+ *
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39438
+ *
+ * The following call uses an "ugly cast" from that discussion to
+ * silence the compiler. The format string is checked for the wrapper
+ * because __attribute__((format)) is specified in the header file.
+ */
+ ((size_t (*)(char *, size_t, const char *, time_t))
+ ipmi_strftime)(datebuf, sizeof(datebuf), fmt, stamp);
+
+ return datebuf;
+}
+
+char *
+ipmi_timestamp_string(uint32_t stamp)
+{
+ if (!ipmi_timestamp_is_valid(stamp)) {
+ return "Unspecified";
+ }
+
+ if (ipmi_timestamp_is_special(stamp)) {
+ if (stamp < SECONDS_A_DAY) {
+ return ipmi_timestamp_fmt(stamp, "S+ %H:%M:%S");
+ }
+ /*
+ * IPMI_TIME_INIT_DONE is over 17 years. This should never
+ * happen normally, but we'll support this anyway.
+ */
+ else {
+ return ipmi_timestamp_fmt(stamp, "S+ %y years %j days %H:%M:%S");
+ }
+ }
+ return ipmi_timestamp_fmt(stamp, "%c %Z");
+}
+
+char *
+ipmi_timestamp_numeric(uint32_t stamp)
+{
+ if (!ipmi_timestamp_is_valid(stamp)) {
+ return "Unspecified";
+ }
+
+ if (ipmi_timestamp_is_special(stamp)) {
+ if (stamp < SECONDS_A_DAY) {
+ return ipmi_timestamp_fmt(stamp, "S+ %H:%M:%S");
+ }
+ /*
+ * IPMI_TIME_INIT_DONE is over 17 years. This should never
+ * happen normally, but we'll support this anyway.
+ */
+ else {
+ return ipmi_timestamp_fmt(stamp, "S+ %y/%j %H:%M:%S");
+ }
+ }
+ return ipmi_timestamp_fmt(stamp, "%x %X %Z");
+}
+
+char *
+ipmi_timestamp_date(uint32_t stamp)
+{
+ if (!ipmi_timestamp_is_valid(stamp)) {
+ return "Unspecified";
+ }
+
+ if (ipmi_timestamp_is_special(stamp)) {
+ return ipmi_timestamp_fmt(stamp, "S+ %y/%j");
+ }
+ return ipmi_timestamp_fmt(stamp, "%x");
+}
+
+char *
+ipmi_timestamp_time(uint32_t stamp)
+{
+ if (!ipmi_timestamp_is_valid(stamp)) {
+ return "Unspecified";
+ }
+
+ /* Format is the same for both normal and special timestamps */
+ return ipmi_timestamp_fmt(stamp, "%X %Z");
+}