1 /* 2 * tmon.c Thermal Monitor (TMON) main function and entry point 3 * 4 * Copyright (C) 2012 Intel Corporation. All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License version 8 * 2 or later as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * Author: Jacob Pan <jacob.jun.pan@linux.intel.com> 16 * 17 */ 18 19 #include <getopt.h> 20 #include <unistd.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <ncurses.h> 27 #include <ctype.h> 28 #include <time.h> 29 #include <signal.h> 30 #include <limits.h> 31 #include <sys/time.h> 32 #include <pthread.h> 33 #include <math.h> 34 #include <stdarg.h> 35 #include <syslog.h> 36 37 #include "tmon.h" 38 39 unsigned long ticktime = 1; /* seconds */ 40 unsigned long no_control = 1; /* monitoring only or use cooling device for 41 * temperature control. 42 */ 43 double time_elapsed = 0.0; 44 unsigned long target_temp_user = 65; /* can be select by tui later */ 45 int dialogue_on; 46 int tmon_exit; 47 static short daemon_mode; 48 static int logging; /* for recording thermal data to a file */ 49 static int debug_on; 50 FILE *tmon_log; 51 /*cooling device used for the PID controller */ 52 char ctrl_cdev[CDEV_NAME_SIZE] = "None"; 53 int target_thermal_zone; /* user selected target zone instance */ 54 static void start_daemon_mode(void); 55 56 pthread_t event_tid; 57 pthread_mutex_t input_lock; 58 void usage() 59 { 60 printf("Usage: tmon [OPTION...]\n"); 61 printf(" -c, --control cooling device in control\n"); 62 printf(" -d, --daemon run as daemon, no TUI\n"); 63 printf(" -g, --debug debug message in syslog\n"); 64 printf(" -h, --help show this help message\n"); 65 printf(" -l, --log log data to /var/tmp/tmon.log\n"); 66 printf(" -t, --time-interval sampling time interval, > 1 sec.\n"); 67 printf(" -v, --version show version\n"); 68 printf(" -z, --zone target thermal zone id\n"); 69 70 exit(0); 71 } 72 73 void version() 74 { 75 printf("TMON version %s\n", VERSION); 76 exit(EXIT_SUCCESS); 77 } 78 79 static void tmon_cleanup(void) 80 { 81 82 syslog(LOG_INFO, "TMON exit cleanup\n"); 83 fflush(stdout); 84 refresh(); 85 if (tmon_log) 86 fclose(tmon_log); 87 if (event_tid) { 88 pthread_mutex_lock(&input_lock); 89 pthread_cancel(event_tid); 90 pthread_mutex_unlock(&input_lock); 91 pthread_mutex_destroy(&input_lock); 92 } 93 closelog(); 94 /* relax control knobs, undo throttling */ 95 set_ctrl_state(0); 96 97 keypad(stdscr, FALSE); 98 echo(); 99 nocbreak(); 100 close_windows(); 101 endwin(); 102 free_thermal_data(); 103 104 exit(1); 105 } 106 107 108 static void tmon_sig_handler(int sig) 109 { 110 syslog(LOG_INFO, "TMON caught signal %d\n", sig); 111 refresh(); 112 switch (sig) { 113 case SIGTERM: 114 printf("sigterm, exit and clean up\n"); 115 fflush(stdout); 116 break; 117 case SIGKILL: 118 printf("sigkill, exit and clean up\n"); 119 fflush(stdout); 120 break; 121 case SIGINT: 122 printf("ctrl-c, exit and clean up\n"); 123 fflush(stdout); 124 break; 125 default: 126 break; 127 } 128 tmon_exit = true; 129 } 130 131 132 static void start_syslog(void) 133 { 134 if (debug_on) 135 setlogmask(LOG_UPTO(LOG_DEBUG)); 136 else 137 setlogmask(LOG_UPTO(LOG_ERR)); 138 openlog("tmon.log", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); 139 syslog(LOG_NOTICE, "TMON started by User %d", getuid()); 140 } 141 142 static void prepare_logging(void) 143 { 144 int i; 145 146 if (!logging) 147 return; 148 /* open local data log file */ 149 tmon_log = fopen(TMON_LOG_FILE, "w+"); 150 if (!tmon_log) { 151 syslog(LOG_ERR, "failed to open log file %s\n", TMON_LOG_FILE); 152 return; 153 } 154 155 fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n"); 156 for (i = 0; i < ptdata.nr_tz_sensor; i++) { 157 char binding_str[33]; /* size of long + 1 */ 158 int j; 159 160 memset(binding_str, 0, sizeof(binding_str)); 161 for (j = 0; j < 32; j++) 162 binding_str[j] = (ptdata.tzi[i].cdev_binding & 1<<j) ? 163 '1' : '0'; 164 165 fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n", 166 ptdata.tzi[i].type, 167 ptdata.tzi[i].instance, 168 binding_str); 169 for (j = 0; j < ptdata.tzi[i].nr_trip_pts; j++) { 170 fprintf(tmon_log, "#\tTP%02d type:%s, temp:%lu\n", j, 171 trip_type_name[ptdata.tzi[i].tp[j].type], 172 ptdata.tzi[i].tp[j].temp); 173 } 174 175 } 176 177 for (i = 0; i < ptdata.nr_cooling_dev; i++) 178 fprintf(tmon_log, "#cooling devices%02d: %s\n", 179 i, ptdata.cdi[i].type); 180 181 fprintf(tmon_log, "#---------- THERMAL DATA LOG STARTED -----------\n"); 182 fprintf(tmon_log, "Samples TargetTemp "); 183 for (i = 0; i < ptdata.nr_tz_sensor; i++) { 184 fprintf(tmon_log, "%s%d ", ptdata.tzi[i].type, 185 ptdata.tzi[i].instance); 186 } 187 for (i = 0; i < ptdata.nr_cooling_dev; i++) 188 fprintf(tmon_log, "%s%d ", ptdata.cdi[i].type, 189 ptdata.cdi[i].instance); 190 191 fprintf(tmon_log, "\n"); 192 } 193 194 static struct option opts[] = { 195 { "control", 1, NULL, 'c' }, 196 { "daemon", 0, NULL, 'd' }, 197 { "time-interval", 1, NULL, 't' }, 198 { "log", 0, NULL, 'l' }, 199 { "help", 0, NULL, 'h' }, 200 { "version", 0, NULL, 'v' }, 201 { "debug", 0, NULL, 'g' }, 202 { 0, 0, NULL, 0 } 203 }; 204 205 206 int main(int argc, char **argv) 207 { 208 int err = 0; 209 int id2 = 0, c; 210 double yk = 0.0; /* controller output */ 211 int target_tz_index; 212 213 if (geteuid() != 0) { 214 printf("TMON needs to be run as root\n"); 215 exit(EXIT_FAILURE); 216 } 217 218 while ((c = getopt_long(argc, argv, "c:dlht:vgz:", opts, &id2)) != -1) { 219 switch (c) { 220 case 'c': 221 no_control = 0; 222 strncpy(ctrl_cdev, optarg, CDEV_NAME_SIZE); 223 break; 224 case 'd': 225 start_daemon_mode(); 226 printf("Run TMON in daemon mode\n"); 227 break; 228 case 't': 229 ticktime = strtod(optarg, NULL); 230 if (ticktime < 1) 231 ticktime = 1; 232 break; 233 case 'l': 234 printf("Logging data to /var/tmp/tmon.log\n"); 235 logging = 1; 236 break; 237 case 'h': 238 usage(); 239 break; 240 case 'v': 241 version(); 242 break; 243 case 'g': 244 debug_on = 1; 245 break; 246 case 'z': 247 target_thermal_zone = strtod(optarg, NULL); 248 break; 249 default: 250 break; 251 } 252 } 253 if (pthread_mutex_init(&input_lock, NULL) != 0) { 254 fprintf(stderr, "\n mutex init failed, exit\n"); 255 return 1; 256 } 257 start_syslog(); 258 if (signal(SIGINT, tmon_sig_handler) == SIG_ERR) 259 syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); 260 if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR) 261 syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); 262 263 if (probe_thermal_sysfs()) { 264 pthread_mutex_destroy(&input_lock); 265 closelog(); 266 return -1; 267 } 268 initialize_curses(); 269 setup_windows(); 270 signal(SIGWINCH, resize_handler); 271 show_title_bar(); 272 show_sensors_w(); 273 show_cooling_device(); 274 update_thermal_data(); 275 show_data_w(); 276 prepare_logging(); 277 init_thermal_controller(); 278 279 nodelay(stdscr, TRUE); 280 err = pthread_create(&event_tid, NULL, &handle_tui_events, NULL); 281 if (err != 0) { 282 printf("\ncan't create thread :[%s]", strerror(err)); 283 tmon_cleanup(); 284 exit(EXIT_FAILURE); 285 } 286 287 /* validate range of user selected target zone, default to the first 288 * instance if out of range 289 */ 290 target_tz_index = zone_instance_to_index(target_thermal_zone); 291 if (target_tz_index < 0) { 292 target_thermal_zone = ptdata.tzi[0].instance; 293 syslog(LOG_ERR, "target zone is not found, default to %d\n", 294 target_thermal_zone); 295 } 296 while (1) { 297 sleep(ticktime); 298 show_title_bar(); 299 show_sensors_w(); 300 update_thermal_data(); 301 if (!dialogue_on) { 302 show_data_w(); 303 show_cooling_device(); 304 } 305 cur_thermal_record++; 306 time_elapsed += ticktime; 307 controller_handler(trec[0].temp[target_tz_index] / 1000, 308 &yk); 309 trec[0].pid_out_pct = yk; 310 if (!dialogue_on) 311 show_control_w(); 312 if (tmon_exit) 313 break; 314 } 315 tmon_cleanup(); 316 return 0; 317 } 318 319 static void start_daemon_mode() 320 { 321 daemon_mode = 1; 322 /* fork */ 323 pid_t sid, pid = fork(); 324 if (pid < 0) { 325 exit(EXIT_FAILURE); 326 } else if (pid > 0) 327 /* kill parent */ 328 exit(EXIT_SUCCESS); 329 330 /* disable TUI, it may not be necessary, but saves some resource */ 331 disable_tui(); 332 333 /* change the file mode mask */ 334 umask(0); 335 336 /* new SID for the daemon process */ 337 sid = setsid(); 338 if (sid < 0) 339 exit(EXIT_FAILURE); 340 341 /* change working directory */ 342 if ((chdir("/")) < 0) 343 exit(EXIT_FAILURE); 344 345 346 sleep(10); 347 348 close(STDIN_FILENO); 349 close(STDOUT_FILENO); 350 close(STDERR_FILENO); 351 352 } 353