Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * tmon.c Thermal Monitor (TMON) main function and entry point
0004  *
0005  * Copyright (C) 2012 Intel Corporation. All rights reserved.
0006  *
0007  * Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
0008  */
0009 
0010 #include <getopt.h>
0011 #include <unistd.h>
0012 #include <stdio.h>
0013 #include <stdlib.h>
0014 #include <string.h>
0015 #include <sys/types.h>
0016 #include <sys/stat.h>
0017 #include <ncurses.h>
0018 #include <ctype.h>
0019 #include <time.h>
0020 #include <signal.h>
0021 #include <limits.h>
0022 #include <sys/time.h>
0023 #include <pthread.h>
0024 #include <math.h>
0025 #include <stdarg.h>
0026 #include <syslog.h>
0027 
0028 #include "tmon.h"
0029 
0030 unsigned long ticktime = 1; /* seconds */
0031 unsigned long no_control = 1; /* monitoring only or use cooling device for
0032                    * temperature control.
0033                    */
0034 double time_elapsed = 0.0;
0035 unsigned long target_temp_user = 65; /* can be select by tui later */
0036 int dialogue_on;
0037 int tmon_exit;
0038 static short    daemon_mode;
0039 static int logging; /* for recording thermal data to a file */
0040 static int debug_on;
0041 FILE *tmon_log;
0042 /*cooling device used for the PID controller */
0043 char ctrl_cdev[CDEV_NAME_SIZE] = "None";
0044 int target_thermal_zone; /* user selected target zone instance */
0045 static void start_daemon_mode(void);
0046 
0047 pthread_t event_tid;
0048 pthread_mutex_t input_lock;
0049 void usage(void)
0050 {
0051     printf("Usage: tmon [OPTION...]\n");
0052     printf("  -c, --control         cooling device in control\n");
0053     printf("  -d, --daemon          run as daemon, no TUI\n");
0054     printf("  -g, --debug           debug message in syslog\n");
0055     printf("  -h, --help            show this help message\n");
0056     printf("  -l, --log             log data to /var/tmp/tmon.log\n");
0057     printf("  -t, --time-interval   sampling time interval, > 1 sec.\n");
0058     printf("  -T, --target-temp     initial target temperature\n");
0059     printf("  -v, --version         show version\n");
0060     printf("  -z, --zone            target thermal zone id\n");
0061 
0062     exit(0);
0063 }
0064 
0065 void version(void)
0066 {
0067     printf("TMON version %s\n", VERSION);
0068     exit(EXIT_SUCCESS);
0069 }
0070 
0071 static void tmon_cleanup(void)
0072 {
0073     syslog(LOG_INFO, "TMON exit cleanup\n");
0074     fflush(stdout);
0075     refresh();
0076     if (tmon_log)
0077         fclose(tmon_log);
0078     if (event_tid) {
0079         pthread_mutex_lock(&input_lock);
0080         pthread_cancel(event_tid);
0081         pthread_mutex_unlock(&input_lock);
0082         pthread_mutex_destroy(&input_lock);
0083     }
0084     closelog();
0085     /* relax control knobs, undo throttling */
0086     set_ctrl_state(0);
0087 
0088     keypad(stdscr, FALSE);
0089     echo();
0090     nocbreak();
0091     close_windows();
0092     endwin();
0093     free_thermal_data();
0094 
0095     exit(1);
0096 }
0097 
0098 static void tmon_sig_handler(int sig)
0099 {
0100     syslog(LOG_INFO, "TMON caught signal %d\n", sig);
0101     refresh();
0102     switch (sig) {
0103     case SIGTERM:
0104         printf("sigterm, exit and clean up\n");
0105         fflush(stdout);
0106         break;
0107     case SIGKILL:
0108         printf("sigkill, exit and clean up\n");
0109         fflush(stdout);
0110         break;
0111     case SIGINT:
0112         printf("ctrl-c, exit and clean up\n");
0113         fflush(stdout);
0114         break;
0115     default:
0116         break;
0117     }
0118     tmon_exit = true;
0119 }
0120 
0121 static void start_syslog(void)
0122 {
0123     if (debug_on)
0124         setlogmask(LOG_UPTO(LOG_DEBUG));
0125     else
0126         setlogmask(LOG_UPTO(LOG_ERR));
0127     openlog("tmon.log", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0);
0128     syslog(LOG_NOTICE, "TMON started by User %d", getuid());
0129 }
0130 
0131 static void prepare_logging(void)
0132 {
0133     int i;
0134     struct stat logstat;
0135 
0136     if (!logging)
0137         return;
0138     /* open local data log file */
0139     tmon_log = fopen(TMON_LOG_FILE, "w+");
0140     if (!tmon_log) {
0141         syslog(LOG_ERR, "failed to open log file %s\n", TMON_LOG_FILE);
0142         return;
0143     }
0144 
0145     if (lstat(TMON_LOG_FILE, &logstat) < 0) {
0146         syslog(LOG_ERR, "Unable to stat log file %s\n", TMON_LOG_FILE);
0147         fclose(tmon_log);
0148         tmon_log = NULL;
0149         return;
0150     }
0151 
0152     /* The log file must be a regular file owned by us */
0153     if (S_ISLNK(logstat.st_mode)) {
0154         syslog(LOG_ERR, "Log file is a symlink.  Will not log\n");
0155         fclose(tmon_log);
0156         tmon_log = NULL;
0157         return;
0158     }
0159 
0160     if (logstat.st_uid != getuid()) {
0161         syslog(LOG_ERR, "We don't own the log file.  Not logging\n");
0162         fclose(tmon_log);
0163         tmon_log = NULL;
0164         return;
0165     }
0166 
0167     fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n");
0168     for (i = 0; i < ptdata.nr_tz_sensor; i++) {
0169         char binding_str[33]; /* size of long + 1 */
0170         int j;
0171 
0172         memset(binding_str, 0, sizeof(binding_str));
0173         for (j = 0; j < 32; j++)
0174             binding_str[j] = (ptdata.tzi[i].cdev_binding & (1 << j)) ?
0175                 '1' : '0';
0176 
0177         fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n",
0178             ptdata.tzi[i].type,
0179             ptdata.tzi[i].instance,
0180             binding_str);
0181         for (j = 0; j < ptdata.tzi[i].nr_trip_pts; j++) {
0182             fprintf(tmon_log, "#\tTP%02d type:%s, temp:%lu\n", j,
0183                 trip_type_name[ptdata.tzi[i].tp[j].type],
0184                 ptdata.tzi[i].tp[j].temp);
0185         }
0186     }
0187 
0188     for (i = 0; i < ptdata.nr_cooling_dev; i++)
0189         fprintf(tmon_log, "#cooling devices%02d: %s\n",
0190             i, ptdata.cdi[i].type);
0191 
0192     fprintf(tmon_log, "#---------- THERMAL DATA LOG STARTED -----------\n");
0193     fprintf(tmon_log, "Samples TargetTemp ");
0194     for (i = 0; i < ptdata.nr_tz_sensor; i++) {
0195         fprintf(tmon_log, "%s%d    ", ptdata.tzi[i].type,
0196             ptdata.tzi[i].instance);
0197     }
0198     for (i = 0; i < ptdata.nr_cooling_dev; i++)
0199         fprintf(tmon_log, "%s%d ", ptdata.cdi[i].type,
0200             ptdata.cdi[i].instance);
0201 
0202     fprintf(tmon_log, "\n");
0203 }
0204 
0205 static struct option opts[] = {
0206     { "control", 1, NULL, 'c' },
0207     { "daemon", 0, NULL, 'd' },
0208     { "time-interval", 1, NULL, 't' },
0209     { "target-temp", 1, NULL, 'T' },
0210     { "log", 0, NULL, 'l' },
0211     { "help", 0, NULL, 'h' },
0212     { "version", 0, NULL, 'v' },
0213     { "debug", 0, NULL, 'g' },
0214     { 0, 0, NULL, 0 }
0215 };
0216 
0217 int main(int argc, char **argv)
0218 {
0219     int err = 0;
0220     int id2 = 0, c;
0221     double yk = 0.0, temp; /* controller output */
0222     int target_tz_index;
0223 
0224     if (geteuid() != 0) {
0225         printf("TMON needs to be run as root\n");
0226         exit(EXIT_FAILURE);
0227     }
0228 
0229     while ((c = getopt_long(argc, argv, "c:dlht:T:vgz:", opts, &id2)) != -1) {
0230         switch (c) {
0231         case 'c':
0232             no_control = 0;
0233             strncpy(ctrl_cdev, optarg, CDEV_NAME_SIZE);
0234             break;
0235         case 'd':
0236             start_daemon_mode();
0237             printf("Run TMON in daemon mode\n");
0238             break;
0239         case 't':
0240             ticktime = strtod(optarg, NULL);
0241             if (ticktime < 1)
0242                 ticktime = 1;
0243             break;
0244         case 'T':
0245             temp = strtod(optarg, NULL);
0246             if (temp < 0) {
0247                 fprintf(stderr, "error: temperature must be positive\n");
0248                 return 1;
0249             }
0250             target_temp_user = temp;
0251             break;
0252         case 'l':
0253             printf("Logging data to /var/tmp/tmon.log\n");
0254             logging = 1;
0255             break;
0256         case 'h':
0257             usage();
0258             break;
0259         case 'v':
0260             version();
0261             break;
0262         case 'g':
0263             debug_on = 1;
0264             break;
0265         case 'z':
0266             target_thermal_zone = strtod(optarg, NULL);
0267             break;
0268         default:
0269             break;
0270         }
0271     }
0272     if (pthread_mutex_init(&input_lock, NULL) != 0) {
0273         fprintf(stderr, "\n mutex init failed, exit\n");
0274         return 1;
0275     }
0276     start_syslog();
0277     if (signal(SIGINT, tmon_sig_handler) == SIG_ERR)
0278         syslog(LOG_DEBUG, "Cannot handle SIGINT\n");
0279     if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR)
0280         syslog(LOG_DEBUG, "Cannot handle SIGTERM\n");
0281 
0282     if (probe_thermal_sysfs()) {
0283         pthread_mutex_destroy(&input_lock);
0284         closelog();
0285         return -1;
0286     }
0287     initialize_curses();
0288     setup_windows();
0289     signal(SIGWINCH, resize_handler);
0290     show_title_bar();
0291     show_sensors_w();
0292     show_cooling_device();
0293     update_thermal_data();
0294     show_data_w();
0295     prepare_logging();
0296     init_thermal_controller();
0297 
0298     nodelay(stdscr, TRUE);
0299     err = pthread_create(&event_tid, NULL, &handle_tui_events, NULL);
0300     if (err != 0) {
0301         printf("\ncan't create thread :[%s]", strerror(err));
0302         tmon_cleanup();
0303         exit(EXIT_FAILURE);
0304     }
0305 
0306     /* validate range of user selected target zone, default to the first
0307      * instance if out of range
0308      */
0309     target_tz_index = zone_instance_to_index(target_thermal_zone);
0310     if (target_tz_index < 0) {
0311         target_thermal_zone = ptdata.tzi[0].instance;
0312         syslog(LOG_ERR, "target zone is not found, default to %d\n",
0313             target_thermal_zone);
0314     }
0315     while (1) {
0316         sleep(ticktime);
0317         show_title_bar();
0318         show_sensors_w();
0319         update_thermal_data();
0320         if (!dialogue_on) {
0321             show_data_w();
0322             show_cooling_device();
0323         }
0324         time_elapsed += ticktime;
0325         controller_handler(trec[0].temp[target_tz_index] / 1000, &yk);
0326         trec[0].pid_out_pct = yk;
0327         if (!dialogue_on)
0328             show_control_w();
0329         if (tmon_exit)
0330             break;
0331     }
0332     tmon_cleanup();
0333     return 0;
0334 }
0335 
0336 static void start_daemon_mode(void)
0337 {
0338     daemon_mode = 1;
0339     /* fork */
0340     pid_t   sid, pid = fork();
0341 
0342     if (pid < 0)
0343         exit(EXIT_FAILURE);
0344     else if (pid > 0)
0345         /* kill parent */
0346         exit(EXIT_SUCCESS);
0347 
0348     /* disable TUI, it may not be necessary, but saves some resource */
0349     disable_tui();
0350 
0351     /* change the file mode mask */
0352     umask(S_IWGRP | S_IWOTH);
0353 
0354     /* new SID for the daemon process */
0355     sid = setsid();
0356     if (sid < 0)
0357         exit(EXIT_FAILURE);
0358 
0359     /* change working directory */
0360     if ((chdir("/")) < 0)
0361         exit(EXIT_FAILURE);
0362 
0363     sleep(10);
0364 
0365     close(STDIN_FILENO);
0366     close(STDOUT_FILENO);
0367     close(STDERR_FILENO);
0368 }