#include "lsfd.h"
+#include <sys/timerfd.h>
+#include <time.h>
+
struct unkn {
struct file file;
const struct anon_ops *anon_ops;
.attach_xinfo = anon_eventpoll_attach_xinfo,
};
+/*
+ * timerfd
+ */
+struct anon_timerfd_data {
+ int clockid;
+ struct itimerspec itimerspec;
+};
+
+static bool anon_timerfd_probe(const char *str)
+{
+ return strncmp(str, "[timerfd]", 9) == 0;
+}
+
+static void anon_timerfd_init(struct unkn *unkn)
+{
+ unkn->anon_data = xcalloc(1, sizeof(struct anon_timerfd_data));
+}
+
+static void anon_timerfd_free(struct unkn *unkn)
+{
+ struct anon_timerfd_data *data = unkn->anon_data;
+ free(data);
+}
+
+static int anon_timerfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
+{
+ struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
+
+ if (strcmp(key, "clockid") == 0) {
+ unsigned long clockid;
+ char *end = NULL;
+
+ errno = 0;
+ clockid = strtoul(value, &end, 0);
+ if (errno != 0)
+ return 0; /* ignore -- parse failed */
+ if (*end != '\0')
+ return 0; /* ignore -- garbage remains. */
+
+ data->clockid = clockid;
+ return 1;
+ } else {
+ struct timespec *t;
+ uint64_t tv_sec;
+ uint64_t tv_nsec;
+
+ if (strcmp(key, "it_value") == 0)
+ t = &data->itimerspec.it_value;
+ else if (strcmp(key, "it_interval") == 0)
+ t = &data->itimerspec.it_interval;
+ else
+ return 0;
+
+ if (sscanf(value, "(%"SCNu64", %"SCNu64")",
+ &tv_sec, &tv_nsec) == 2) {
+ t->tv_sec = (time_t)tv_sec;
+ t->tv_nsec = (long)tv_nsec;
+ return 1;
+ }
+
+ return 0;
+ }
+}
+
+static bool is_zero_timespec(const struct timespec *t)
+{
+ return !t->tv_sec && !t->tv_nsec;
+}
+
+static const char *anon_timerfd_decode_clockid(int clockid)
+{
+ switch (clockid) {
+ case CLOCK_REALTIME:
+ return "realtime";
+ case CLOCK_MONOTONIC:
+ return "monotonic";
+ case CLOCK_BOOTTIME:
+ return "boottime";
+ case CLOCK_REALTIME_ALARM:
+ return "realtime-alarm";
+ case CLOCK_BOOTTIME_ALARM:
+ return "boottime-alarm";
+ default:
+ return "unknown";
+ }
+}
+
+static void anon_timerfd_render_timespec_string(char *buf, size_t size,
+ const char *prefix,
+ const struct timespec *t)
+{
+ snprintf(buf, size, "%s%llu.%09ld",
+ prefix? prefix: "",
+ (unsigned long long)t->tv_sec, t->tv_nsec);
+}
+
+static char *anon_timerfd_get_name(struct unkn *unkn)
+{
+ char *str = NULL;
+
+ struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
+ const struct timespec *exp;
+ const struct timespec *ival;
+
+ const char *clockid_name;
+ char exp_buf[BUFSIZ] = {'\0'};
+ char ival_buf[BUFSIZ] = {'\0'};
+
+ clockid_name = anon_timerfd_decode_clockid(data->clockid);
+
+ exp = &data->itimerspec.it_value;
+ if (!is_zero_timespec(exp))
+ anon_timerfd_render_timespec_string(exp_buf, sizeof(exp_buf),
+ " remaining=", exp);
+
+ ival = &data->itimerspec.it_interval;
+ if (!is_zero_timespec(ival))
+ anon_timerfd_render_timespec_string(ival_buf, sizeof(ival_buf),
+ " interval=", ival);
+
+ xasprintf(&str, "clockid=%s%s%s", clockid_name, exp_buf, ival_buf);
+ return str;
+}
+
+static bool anon_timerfd_fill_column(struct proc *proc __attribute__((__unused__)),
+ struct unkn *unkn,
+ struct libscols_line *ln __attribute__((__unused__)),
+ int column_id,
+ size_t column_index __attribute__((__unused__)),
+ char **str)
+{
+ struct anon_timerfd_data *data = (struct anon_timerfd_data *)unkn->anon_data;
+ char buf[BUFSIZ] = {'\0'};
+
+ switch(column_id) {
+ case COL_TIMERFD_CLOCKID:
+ *str = xstrdup(anon_timerfd_decode_clockid(data->clockid));
+ return true;
+ case COL_TIMERFD_INTERVAL:
+ anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL,
+ &data->itimerspec.it_interval);
+ *str = xstrdup(buf);
+ return true;
+ case COL_TIMERFD_REMAINING:
+ anon_timerfd_render_timespec_string(buf, sizeof(buf), NULL,
+ &data->itimerspec.it_value);
+ *str = xstrdup(buf);
+ return true;
+ }
+
+ return false;
+}
+
+static const struct anon_ops anon_timerfd_ops = {
+ .class = "timerfd",
+ .probe = anon_timerfd_probe,
+ .get_name = anon_timerfd_get_name,
+ .fill_column = anon_timerfd_fill_column,
+ .init = anon_timerfd_init,
+ .free = anon_timerfd_free,
+ .handle_fdinfo = anon_timerfd_handle_fdinfo,
+};
+
/*
* generic (fallback implementation)
*/
&anon_pidfd_ops,
&anon_eventfd_ops,
&anon_eventpoll_ops,
+ &anon_timerfd_ops,
};
static const struct anon_ops *anon_probe(const char *str)
[COL_TID] = { "TID",
5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
N_("thread ID of the process opening the file") },
+ [COL_TIMERFD_CLOCKID] = { "TIMERFD.CLOCKID",
+ 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
+ N_("clockid") },
+ [COL_TIMERFD_INTERVAL] = { "TIMERFD.INTERVAL",
+ 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
+ N_("interval") },
+ [COL_TIMERFD_REMAINING]= { "TIMERFD.REMAINING",
+ 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
+ N_("remaining time") },
[COL_TYPE] = { "TYPE",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("file type (cooked)") },