#include #include #include #include #include #include #include #define LOG_MODULE "clock" #define LOG_ENABLE_DBG 0 #include "../log.h" #include "../bar/bar.h" #include "../config.h" #include "../config-verify.h" #include "../plugin.h" struct private { struct particle *label; enum { UPDATE_GRANULARITY_SECONDS, UPDATE_GRANULARITY_MINUTES, } update_granularity; char *date_format; char *time_format; bool utc; }; static void destroy(struct module *mod) { struct private *m = mod->private; m->label->destroy(m->label); free(m->time_format); free(m->date_format); free(m); module_default_destroy(mod); } static const char * description(const struct module *mod) { return "clock"; } static struct exposable * content(struct module *mod) { const struct private *m = mod->private; time_t t = time(NULL); struct tm *tm = m->utc ? gmtime(&t) : localtime(&t); char date_str[1024]; strftime(date_str, sizeof(date_str), m->date_format, tm); char time_str[1024]; strftime(time_str, sizeof(time_str), m->time_format, tm); struct tag_set tags = { .tags = (struct tag *[]){tag_new_string(mod, "time", time_str), tag_new_string(mod, "date", date_str)}, .count = 2, }; struct exposable *exposable = m->label->instantiate(m->label, &tags); tag_set_destroy(&tags); return exposable; } static int run(struct module *mod) { const struct private *m = mod->private; const struct bar *bar = mod->bar; bar->refresh(bar); int ret = 1; while (true) { struct timespec _now; clock_gettime(CLOCK_REALTIME, &_now); const struct timeval now = { .tv_sec = _now.tv_sec, .tv_usec = _now.tv_nsec / 1000, }; int timeout_ms = 1000; switch (m->update_granularity) { case UPDATE_GRANULARITY_SECONDS: { const struct timeval next_second = { .tv_sec = now.tv_sec + 1, .tv_usec = 0}; struct timeval _timeout; timersub(&next_second, &now, &_timeout); assert(_timeout.tv_sec == 0 || (_timeout.tv_sec == 1 && _timeout.tv_usec == 0)); timeout_ms = _timeout.tv_usec / 1000; break; } case UPDATE_GRANULARITY_MINUTES: { const struct timeval next_minute = { .tv_sec = now.tv_sec / 60 * 60 + 60, .tv_usec = 0, }; struct timeval _timeout; timersub(&next_minute, &now, &_timeout); timeout_ms = _timeout.tv_sec * 1000 + _timeout.tv_usec / 1000; } } /* Add 1ms to account for rounding errors */ timeout_ms++; LOG_DBG("now: %lds %ldµs -> timeout: %dms", now.tv_sec, now.tv_usec, timeout_ms); struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; if (poll(fds, 1, timeout_ms) < 0) { if (errno == EINTR) continue; LOG_ERRNO("failed to poll"); break; } if (fds[0].revents & POLLIN) { ret = 0; break; } bar->refresh(bar); } return ret; } static struct module * clock_new(struct particle *label, const char *date_format, const char *time_format, bool utc) { struct private *m = calloc(1, sizeof(*m)); m->label = label; m->date_format = strdup(date_format); m->time_format = strdup(time_format); m->utc = utc; static const char *const seconds_formatters[] = { "%c", "%s", "%S", "%T", "%r", "%X", }; m->update_granularity = UPDATE_GRANULARITY_MINUTES; for (size_t i = 0; i < sizeof(seconds_formatters) / sizeof(seconds_formatters[0]); i++) { if (strstr(time_format, seconds_formatters[i]) != NULL) { m->update_granularity = UPDATE_GRANULARITY_SECONDS; break; } } LOG_DBG("using %s update granularity", (m->update_granularity == UPDATE_GRANULARITY_MINUTES ? "minutes" : "seconds")); struct module *mod = module_common_new(); mod->private = m; mod->run = &run; mod->destroy = &destroy; mod->content = &content; mod->description = &description; return mod; } static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *date_format = yml_get_value(node, "date-format"); const struct yml_node *time_format = yml_get_value(node, "time-format"); const struct yml_node *utc = yml_get_value(node, "utc"); return clock_new( conf_to_particle(c, inherited), date_format != NULL ? yml_value_as_string(date_format) : "%x", time_format != NULL ? yml_value_as_string(time_format) : "%H:%M", utc != NULL ? yml_value_as_bool(utc) : false); } static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"date-format", false, &conf_verify_string}, {"time-format", false, &conf_verify_string}, {"utc", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; return conf_verify_dict(chain, node, attrs); } const struct module_iface module_clock_iface = { .verify_conf = &verify_conf, .from_conf = &from_conf, }; #if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) extern const struct module_iface iface __attribute__((weak, alias("module_clock_iface"))); #endif