162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 362306a36Sopenharmony_ci#define _GNU_SOURCE 462306a36Sopenharmony_ci#include <dirent.h> 562306a36Sopenharmony_ci#include <fcntl.h> 662306a36Sopenharmony_ci#include <getopt.h> 762306a36Sopenharmony_ci#include <regex.h> 862306a36Sopenharmony_ci#include <signal.h> 962306a36Sopenharmony_ci#include <stdio.h> 1062306a36Sopenharmony_ci#include <stdlib.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <sys/stat.h> 1362306a36Sopenharmony_ci#include <sys/signalfd.h> 1462306a36Sopenharmony_ci#include <sys/timerfd.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include <sys/wait.h> 1762306a36Sopenharmony_ci#include <time.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <linux/thermal.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <libconfig.h> 2262306a36Sopenharmony_ci#include "thermal-tools.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define CLASS_THERMAL "/sys/class/thermal" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cienum { 2762306a36Sopenharmony_ci THERMOMETER_SUCCESS = 0, 2862306a36Sopenharmony_ci THERMOMETER_OPTION_ERROR, 2962306a36Sopenharmony_ci THERMOMETER_LOG_ERROR, 3062306a36Sopenharmony_ci THERMOMETER_CONFIG_ERROR, 3162306a36Sopenharmony_ci THERMOMETER_TIME_ERROR, 3262306a36Sopenharmony_ci THERMOMETER_INIT_ERROR, 3362306a36Sopenharmony_ci THERMOMETER_RUNTIME_ERROR 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct options { 3762306a36Sopenharmony_ci int loglvl; 3862306a36Sopenharmony_ci int logopt; 3962306a36Sopenharmony_ci int overwrite; 4062306a36Sopenharmony_ci int duration; 4162306a36Sopenharmony_ci const char *config; 4262306a36Sopenharmony_ci char postfix[PATH_MAX]; 4362306a36Sopenharmony_ci char output[PATH_MAX]; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct tz_regex { 4762306a36Sopenharmony_ci regex_t regex; 4862306a36Sopenharmony_ci int polling; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct configuration { 5262306a36Sopenharmony_ci struct tz_regex *tz_regex; 5362306a36Sopenharmony_ci int nr_tz_regex; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct tz { 5862306a36Sopenharmony_ci FILE *file_out; 5962306a36Sopenharmony_ci int fd_temp; 6062306a36Sopenharmony_ci int fd_timer; 6162306a36Sopenharmony_ci int polling; 6262306a36Sopenharmony_ci const char *name; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct thermometer { 6662306a36Sopenharmony_ci struct tz *tz; 6762306a36Sopenharmony_ci int nr_tz; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct tz_regex *configuration_tz_match(const char *expr, 7162306a36Sopenharmony_ci struct configuration *config) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci int i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (i = 0; i < config->nr_tz_regex; i++) { 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!regexec(&config->tz_regex[i].regex, expr, 0, NULL, 0)) 7862306a36Sopenharmony_ci return &config->tz_regex[i]; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return NULL; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int configuration_default_init(struct configuration *config) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci config->tz_regex = realloc(config->tz_regex, sizeof(*config->tz_regex) * 8762306a36Sopenharmony_ci (config->nr_tz_regex + 1)); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (regcomp(&config->tz_regex[config->nr_tz_regex].regex, ".*", 9062306a36Sopenharmony_ci REG_NOSUB | REG_EXTENDED)) { 9162306a36Sopenharmony_ci ERROR("Invalid regular expression\n"); 9262306a36Sopenharmony_ci return -1; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci config->tz_regex[config->nr_tz_regex].polling = 250; 9662306a36Sopenharmony_ci config->nr_tz_regex = 1; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int configuration_init(const char *path, struct configuration *config) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci config_t cfg; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci config_setting_t *tz; 10662306a36Sopenharmony_ci int i, length; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (path && access(path, F_OK)) { 10962306a36Sopenharmony_ci ERROR("'%s' is not accessible\n", path); 11062306a36Sopenharmony_ci return -1; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!path && !config->nr_tz_regex) { 11462306a36Sopenharmony_ci INFO("No thermal zones configured, using wildcard for all of them\n"); 11562306a36Sopenharmony_ci return configuration_default_init(config); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci config_init(&cfg); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!config_read_file(&cfg, path)) { 12162306a36Sopenharmony_ci ERROR("Failed to parse %s:%d - %s\n", config_error_file(&cfg), 12262306a36Sopenharmony_ci config_error_line(&cfg), config_error_text(&cfg)); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return -1; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci tz = config_lookup(&cfg, "thermal-zones"); 12862306a36Sopenharmony_ci if (!tz) { 12962306a36Sopenharmony_ci ERROR("No thermal zone configured to be monitored\n"); 13062306a36Sopenharmony_ci return -1; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci length = config_setting_length(tz); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci INFO("Found %d thermal zone(s) regular expression\n", length); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = 0; i < length; i++) { 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci config_setting_t *node; 14062306a36Sopenharmony_ci const char *name; 14162306a36Sopenharmony_ci int polling; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci node = config_setting_get_elem(tz, i); 14462306a36Sopenharmony_ci if (!node) { 14562306a36Sopenharmony_ci ERROR("Missing node name '%d'\n", i); 14662306a36Sopenharmony_ci return -1; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!config_setting_lookup_string(node, "name", &name)) { 15062306a36Sopenharmony_ci ERROR("Thermal zone name not found\n"); 15162306a36Sopenharmony_ci return -1; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!config_setting_lookup_int(node, "polling", &polling)) { 15562306a36Sopenharmony_ci ERROR("Polling value not found"); 15662306a36Sopenharmony_ci return -1; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci config->tz_regex = realloc(config->tz_regex, sizeof(*config->tz_regex) * 16062306a36Sopenharmony_ci (config->nr_tz_regex + 1)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (regcomp(&config->tz_regex[config->nr_tz_regex].regex, name, 16362306a36Sopenharmony_ci REG_NOSUB | REG_EXTENDED)) { 16462306a36Sopenharmony_ci ERROR("Invalid regular expression '%s'\n", name); 16562306a36Sopenharmony_ci continue; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci config->tz_regex[config->nr_tz_regex].polling = polling; 16962306a36Sopenharmony_ci config->nr_tz_regex++; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci INFO("Thermal zone regular expression '%s' with polling %d\n", 17262306a36Sopenharmony_ci name, polling); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void usage(const char *cmd) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci printf("%s Version: %s\n", cmd, VERSION); 18162306a36Sopenharmony_ci printf("Usage: %s [options]\n", cmd); 18262306a36Sopenharmony_ci printf("\t-h, --help\t\tthis help\n"); 18362306a36Sopenharmony_ci printf("\t-o, --output <dir>\toutput directory for temperature capture\n"); 18462306a36Sopenharmony_ci printf("\t-c, --config <file>\tconfiguration file\n"); 18562306a36Sopenharmony_ci printf("\t-d, --duration <seconds>\tcapture duration\n"); 18662306a36Sopenharmony_ci printf("\t-l, --loglevel <level>\tlog level: "); 18762306a36Sopenharmony_ci printf("DEBUG, INFO, NOTICE, WARN, ERROR\n"); 18862306a36Sopenharmony_ci printf("\t-p, --postfix <string>\tpostfix to be happened at the end of the files\n"); 18962306a36Sopenharmony_ci printf("\t-s, --syslog\t\toutput to syslog\n"); 19062306a36Sopenharmony_ci printf("\t-w, --overwrite\t\toverwrite the temperature capture files if they exist\n"); 19162306a36Sopenharmony_ci printf("\n"); 19262306a36Sopenharmony_ci exit(0); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int options_init(int argc, char *argv[], struct options *options) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci int opt; 19862306a36Sopenharmony_ci time_t now = time(NULL); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci struct option long_options[] = { 20162306a36Sopenharmony_ci { "help", no_argument, NULL, 'h' }, 20262306a36Sopenharmony_ci { "config", required_argument, NULL, 'c' }, 20362306a36Sopenharmony_ci { "duration", required_argument, NULL, 'd' }, 20462306a36Sopenharmony_ci { "loglevel", required_argument, NULL, 'l' }, 20562306a36Sopenharmony_ci { "postfix", required_argument, NULL, 'p' }, 20662306a36Sopenharmony_ci { "output", required_argument, NULL, 'o' }, 20762306a36Sopenharmony_ci { "syslog", required_argument, NULL, 's' }, 20862306a36Sopenharmony_ci { "overwrite", no_argument, NULL, 'w' }, 20962306a36Sopenharmony_ci { 0, 0, 0, 0 } 21062306a36Sopenharmony_ci }; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci strftime(options->postfix, sizeof(options->postfix), 21362306a36Sopenharmony_ci "-%Y-%m-%d_%H:%M:%S", gmtime(&now)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci while (1) { 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci int optindex = 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci opt = getopt_long(argc, argv, "ho:c:d:l:p:sw", long_options, &optindex); 22062306a36Sopenharmony_ci if (opt == -1) 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (opt) { 22462306a36Sopenharmony_ci case 'c': 22562306a36Sopenharmony_ci options->config = optarg; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case 'd': 22862306a36Sopenharmony_ci options->duration = atoi(optarg) * 1000; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case 'l': 23162306a36Sopenharmony_ci options->loglvl = log_str2level(optarg); 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case 'h': 23462306a36Sopenharmony_ci usage(basename(argv[0])); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case 'p': 23762306a36Sopenharmony_ci strcpy(options->postfix, optarg); 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case 'o': 24062306a36Sopenharmony_ci strcpy(options->output, optarg); 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case 's': 24362306a36Sopenharmony_ci options->logopt = TO_SYSLOG; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case 'w': 24662306a36Sopenharmony_ci options->overwrite = 1; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci default: /* '?' */ 24962306a36Sopenharmony_ci ERROR("Usage: %s --help\n", argv[0]); 25062306a36Sopenharmony_ci return -1; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int thermometer_add_tz(const char *path, const char *name, int polling, 25862306a36Sopenharmony_ci struct thermometer *thermometer) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int fd; 26162306a36Sopenharmony_ci char tz_path[PATH_MAX]; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sprintf(tz_path, CLASS_THERMAL"/%s/temp", path); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci fd = open(tz_path, O_RDONLY); 26662306a36Sopenharmony_ci if (fd < 0) { 26762306a36Sopenharmony_ci ERROR("Failed to open '%s': %m\n", tz_path); 26862306a36Sopenharmony_ci return -1; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci thermometer->tz = realloc(thermometer->tz, 27262306a36Sopenharmony_ci sizeof(*thermometer->tz) * (thermometer->nr_tz + 1)); 27362306a36Sopenharmony_ci if (!thermometer->tz) { 27462306a36Sopenharmony_ci ERROR("Failed to allocate thermometer->tz\n"); 27562306a36Sopenharmony_ci return -1; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci thermometer->tz[thermometer->nr_tz].fd_temp = fd; 27962306a36Sopenharmony_ci thermometer->tz[thermometer->nr_tz].name = strdup(name); 28062306a36Sopenharmony_ci thermometer->tz[thermometer->nr_tz].polling = polling; 28162306a36Sopenharmony_ci thermometer->nr_tz++; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci INFO("Added thermal zone '%s->%s (polling:%d)'\n", path, name, polling); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int thermometer_init(struct configuration *config, 28962306a36Sopenharmony_ci struct thermometer *thermometer) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci DIR *dir; 29262306a36Sopenharmony_ci struct dirent *dirent; 29362306a36Sopenharmony_ci struct tz_regex *tz_regex; 29462306a36Sopenharmony_ci const char *tz_dirname = "thermal_zone"; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (mainloop_init()) { 29762306a36Sopenharmony_ci ERROR("Failed to start mainloop\n"); 29862306a36Sopenharmony_ci return -1; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci dir = opendir(CLASS_THERMAL); 30262306a36Sopenharmony_ci if (!dir) { 30362306a36Sopenharmony_ci ERROR("failed to open '%s'\n", CLASS_THERMAL); 30462306a36Sopenharmony_ci return -1; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci while ((dirent = readdir(dir))) { 30862306a36Sopenharmony_ci char tz_type[THERMAL_NAME_LENGTH]; 30962306a36Sopenharmony_ci char tz_path[PATH_MAX]; 31062306a36Sopenharmony_ci FILE *tz_file; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (strncmp(dirent->d_name, tz_dirname, strlen(tz_dirname))) 31362306a36Sopenharmony_ci continue; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci sprintf(tz_path, CLASS_THERMAL"/%s/type", dirent->d_name); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci tz_file = fopen(tz_path, "r"); 31862306a36Sopenharmony_ci if (!tz_file) { 31962306a36Sopenharmony_ci ERROR("Failed to open '%s': %m", tz_path); 32062306a36Sopenharmony_ci continue; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci fscanf(tz_file, "%s", tz_type); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci fclose(tz_file); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci tz_regex = configuration_tz_match(tz_type, config); 32862306a36Sopenharmony_ci if (!tz_regex) 32962306a36Sopenharmony_ci continue; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (thermometer_add_tz(dirent->d_name, tz_type, 33262306a36Sopenharmony_ci tz_regex->polling, thermometer)) 33362306a36Sopenharmony_ci continue; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci closedir(dir); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int timer_temperature_callback(int fd, void *arg) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct tz *tz = arg; 34462306a36Sopenharmony_ci char buf[16] = { 0 }; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci pread(tz->fd_temp, buf, sizeof(buf), 0); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci fprintf(tz->file_out, "%ld %s", getuptimeofday_ms(), buf); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci read(fd, buf, sizeof(buf)); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int thermometer_start(struct thermometer *thermometer, 35662306a36Sopenharmony_ci struct options *options) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct itimerspec timer_it = { 0 }; 35962306a36Sopenharmony_ci char *path; 36062306a36Sopenharmony_ci FILE *f; 36162306a36Sopenharmony_ci int i; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci INFO("Capturing %d thermal zone(s) temperature...\n", thermometer->nr_tz); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (access(options->output, F_OK) && mkdir(options->output, 0700)) { 36662306a36Sopenharmony_ci ERROR("Failed to create directory '%s'\n", options->output); 36762306a36Sopenharmony_ci return -1; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci for (i = 0; i < thermometer->nr_tz; i++) { 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci asprintf(&path, "%s/%s%s", options->output, 37362306a36Sopenharmony_ci thermometer->tz[i].name, options->postfix); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (!options->overwrite && !access(path, F_OK)) { 37662306a36Sopenharmony_ci ERROR("'%s' already exists\n", path); 37762306a36Sopenharmony_ci return -1; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci f = fopen(path, "w"); 38162306a36Sopenharmony_ci if (!f) { 38262306a36Sopenharmony_ci ERROR("Failed to create '%s':%m\n", path); 38362306a36Sopenharmony_ci return -1; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci fprintf(f, "timestamp(ms) %s(°mC)\n", thermometer->tz[i].name); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci thermometer->tz[i].file_out = f; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci DEBUG("Created '%s' file for thermal zone '%s'\n", path, thermometer->tz[i].name); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * Create polling timer 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci thermometer->tz[i].fd_timer = timerfd_create(CLOCK_MONOTONIC, 0); 39662306a36Sopenharmony_ci if (thermometer->tz[i].fd_timer < 0) { 39762306a36Sopenharmony_ci ERROR("Failed to create timer for '%s': %m\n", 39862306a36Sopenharmony_ci thermometer->tz[i].name); 39962306a36Sopenharmony_ci return -1; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci DEBUG("Watching '%s' every %d ms\n", 40362306a36Sopenharmony_ci thermometer->tz[i].name, thermometer->tz[i].polling); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci timer_it.it_interval = timer_it.it_value = 40662306a36Sopenharmony_ci msec_to_timespec(thermometer->tz[i].polling); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (timerfd_settime(thermometer->tz[i].fd_timer, 0, 40962306a36Sopenharmony_ci &timer_it, NULL) < 0) 41062306a36Sopenharmony_ci return -1; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (mainloop_add(thermometer->tz[i].fd_timer, 41362306a36Sopenharmony_ci timer_temperature_callback, 41462306a36Sopenharmony_ci &thermometer->tz[i])) 41562306a36Sopenharmony_ci return -1; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int thermometer_execute(int argc, char *argv[], char *const envp[], pid_t *pid) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci if (!argc) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci *pid = fork(); 42762306a36Sopenharmony_ci if (*pid < 0) { 42862306a36Sopenharmony_ci ERROR("Failed to fork process: %m"); 42962306a36Sopenharmony_ci return -1; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!(*pid)) { 43362306a36Sopenharmony_ci execvpe(argv[0], argv, envp); 43462306a36Sopenharmony_ci exit(1); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int kill_process(__maybe_unused int fd, void *arg) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci pid_t pid = *(pid_t *)arg; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (kill(pid, SIGTERM)) 44562306a36Sopenharmony_ci ERROR("Failed to send SIGTERM signal to '%d': %p\n", pid); 44662306a36Sopenharmony_ci else if (waitpid(pid, NULL, 0)) 44762306a36Sopenharmony_ci ERROR("Failed to wait pid '%d': %p\n", pid); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci mainloop_exit(); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int exit_mainloop(__maybe_unused int fd, __maybe_unused void *arg) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci mainloop_exit(); 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int thermometer_wait(struct options *options, pid_t pid) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int fd; 46362306a36Sopenharmony_ci sigset_t mask; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* 46662306a36Sopenharmony_ci * If there is a duration specified, we will exit the mainloop 46762306a36Sopenharmony_ci * and gracefully close all the files which will flush the 46862306a36Sopenharmony_ci * file system cache 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci if (options->duration) { 47162306a36Sopenharmony_ci struct itimerspec timer_it = { 0 }; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci timer_it.it_value = msec_to_timespec(options->duration); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci fd = timerfd_create(CLOCK_MONOTONIC, 0); 47662306a36Sopenharmony_ci if (fd < 0) { 47762306a36Sopenharmony_ci ERROR("Failed to create duration timer: %m\n"); 47862306a36Sopenharmony_ci return -1; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (timerfd_settime(fd, 0, &timer_it, NULL)) { 48262306a36Sopenharmony_ci ERROR("Failed to set timer time: %m\n"); 48362306a36Sopenharmony_ci return -1; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (mainloop_add(fd, pid < 0 ? exit_mainloop : kill_process, &pid)) { 48762306a36Sopenharmony_ci ERROR("Failed to set timer exit mainloop callback\n"); 48862306a36Sopenharmony_ci return -1; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* 49362306a36Sopenharmony_ci * We want to catch any keyboard interrupt, as well as child 49462306a36Sopenharmony_ci * signals if any in order to exit properly 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci sigemptyset(&mask); 49762306a36Sopenharmony_ci sigaddset(&mask, SIGINT); 49862306a36Sopenharmony_ci sigaddset(&mask, SIGQUIT); 49962306a36Sopenharmony_ci sigaddset(&mask, SIGCHLD); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (sigprocmask(SIG_BLOCK, &mask, NULL)) { 50262306a36Sopenharmony_ci ERROR("Failed to set sigprocmask: %m\n"); 50362306a36Sopenharmony_ci return -1; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci fd = signalfd(-1, &mask, 0); 50762306a36Sopenharmony_ci if (fd < 0) { 50862306a36Sopenharmony_ci ERROR("Failed to set the signalfd: %m\n"); 50962306a36Sopenharmony_ci return -1; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (mainloop_add(fd, exit_mainloop, NULL)) { 51362306a36Sopenharmony_ci ERROR("Failed to set timer exit mainloop callback\n"); 51462306a36Sopenharmony_ci return -1; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return mainloop(-1); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int thermometer_stop(struct thermometer *thermometer) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci int i; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci INFO("Closing/flushing output files\n"); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci for (i = 0; i < thermometer->nr_tz; i++) 52762306a36Sopenharmony_ci fclose(thermometer->tz[i].file_out); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ciint main(int argc, char *argv[], char *const envp[]) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct options options = { 53562306a36Sopenharmony_ci .loglvl = LOG_DEBUG, 53662306a36Sopenharmony_ci .logopt = TO_STDOUT, 53762306a36Sopenharmony_ci .output = ".", 53862306a36Sopenharmony_ci }; 53962306a36Sopenharmony_ci struct configuration config = { 0 }; 54062306a36Sopenharmony_ci struct thermometer thermometer = { 0 }; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci pid_t pid = -1; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (options_init(argc, argv, &options)) 54562306a36Sopenharmony_ci return THERMOMETER_OPTION_ERROR; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (log_init(options.loglvl, argv[0], options.logopt)) 54862306a36Sopenharmony_ci return THERMOMETER_LOG_ERROR; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (configuration_init(options.config, &config)) 55162306a36Sopenharmony_ci return THERMOMETER_CONFIG_ERROR; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (uptimeofday_init()) 55462306a36Sopenharmony_ci return THERMOMETER_TIME_ERROR; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (thermometer_init(&config, &thermometer)) 55762306a36Sopenharmony_ci return THERMOMETER_INIT_ERROR; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (thermometer_start(&thermometer, &options)) 56062306a36Sopenharmony_ci return THERMOMETER_RUNTIME_ERROR; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (thermometer_execute(argc - optind, &argv[optind], envp, &pid)) 56362306a36Sopenharmony_ci return THERMOMETER_RUNTIME_ERROR; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (thermometer_wait(&options, pid)) 56662306a36Sopenharmony_ci return THERMOMETER_RUNTIME_ERROR; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (thermometer_stop(&thermometer)) 56962306a36Sopenharmony_ci return THERMOMETER_RUNTIME_ERROR; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return THERMOMETER_SUCCESS; 57262306a36Sopenharmony_ci} 573