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 struct stat logstat; 146 147 if (!logging) 148 return; 149 /* open local data log file */ 150 tmon_log = fopen(TMON_LOG_FILE, "w+"); 151 if (!tmon_log) { 152 syslog(LOG_ERR, "failed to open log file %s\n", TMON_LOG_FILE); 153 return; 154 } 155 156 if (lstat(TMON_LOG_FILE, &logstat) < 0) { 157 syslog(LOG_ERR, "Unable to stat log file %s\n", TMON_LOG_FILE); 158 fclose(tmon_log); 159 tmon_log = NULL; 160 return; 161 } 162 163 /* The log file must be a regular file owned by us */ 164 if (S_ISLNK(logstat.st_mode)) { 165 syslog(LOG_ERR, "Log file is a symlink. Will not log\n"); 166 fclose(tmon_log); 167 tmon_log = NULL; 168 return; 169 } 170 171 if (logstat.st_uid != getuid()) { 172 syslog(LOG_ERR, "We don't own the log file. Not logging\n"); 173 fclose(tmon_log); 174 tmon_log = NULL; 175 return; 176 } 177 178 179 fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n"); 180 for (i = 0; i < ptdata.nr_tz_sensor; i++) { 181 char binding_str[33]; /* size of long + 1 */ 182 int j; 183 184 memset(binding_str, 0, sizeof(binding_str)); 185 for (j = 0; j < 32; j++) 186 binding_str[j] = (ptdata.tzi[i].cdev_binding & 1<<j) ? 187 '1' : '0'; 188 189 fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n", 190 ptdata.tzi[i].type, 191 ptdata.tzi[i].instance, 192 binding_str); 193 for (j = 0; j < ptdata.tzi[i].nr_trip_pts; j++) { 194 fprintf(tmon_log, "#\tTP%02d type:%s, temp:%lu\n", j, 195 trip_type_name[ptdata.tzi[i].tp[j].type], 196 ptdata.tzi[i].tp[j].temp); 197 } 198 199 } 200 201 for (i = 0; i < ptdata.nr_cooling_dev; i++) 202 fprintf(tmon_log, "#cooling devices%02d: %s\n", 203 i, ptdata.cdi[i].type); 204 205 fprintf(tmon_log, "#---------- THERMAL DATA LOG STARTED -----------\n"); 206 fprintf(tmon_log, "Samples TargetTemp "); 207 for (i = 0; i < ptdata.nr_tz_sensor; i++) { 208 fprintf(tmon_log, "%s%d ", ptdata.tzi[i].type, 209 ptdata.tzi[i].instance); 210 } 211 for (i = 0; i < ptdata.nr_cooling_dev; i++) 212 fprintf(tmon_log, "%s%d ", ptdata.cdi[i].type, 213 ptdata.cdi[i].instance); 214 215 fprintf(tmon_log, "\n"); 216 } 217 218 static struct option opts[] = { 219 { "control", 1, NULL, 'c' }, 220 { "daemon", 0, NULL, 'd' }, 221 { "time-interval", 1, NULL, 't' }, 222 { "log", 0, NULL, 'l' }, 223 { "help", 0, NULL, 'h' }, 224 { "version", 0, NULL, 'v' }, 225 { "debug", 0, NULL, 'g' }, 226 { 0, 0, NULL, 0 } 227 }; 228 229 230 int main(int argc, char **argv) 231 { 232 int err = 0; 233 int id2 = 0, c; 234 double yk = 0.0; /* controller output */ 235 int target_tz_index; 236 237 if (geteuid() != 0) { 238 printf("TMON needs to be run as root\n"); 239 exit(EXIT_FAILURE); 240 } 241 242 while ((c = getopt_long(argc, argv, "c:dlht:vgz:", opts, &id2)) != -1) { 243 switch (c) { 244 case 'c': 245 no_control = 0; 246 strncpy(ctrl_cdev, optarg, CDEV_NAME_SIZE); 247 break; 248 case 'd': 249 start_daemon_mode(); 250 printf("Run TMON in daemon mode\n"); 251 break; 252 case 't': 253 ticktime = strtod(optarg, NULL); 254 if (ticktime < 1) 255 ticktime = 1; 256 break; 257 case 'l': 258 printf("Logging data to /var/tmp/tmon.log\n"); 259 logging = 1; 260 break; 261 case 'h': 262 usage(); 263 break; 264 case 'v': 265 version(); 266 break; 267 case 'g': 268 debug_on = 1; 269 break; 270 case 'z': 271 target_thermal_zone = strtod(optarg, NULL); 272 break; 273 default: 274 break; 275 } 276 } 277 if (pthread_mutex_init(&input_lock, NULL) != 0) { 278 fprintf(stderr, "\n mutex init failed, exit\n"); 279 return 1; 280 } 281 start_syslog(); 282 if (signal(SIGINT, tmon_sig_handler) == SIG_ERR) 283 syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); 284 if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR) 285 syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); 286 287 if (probe_thermal_sysfs()) { 288 pthread_mutex_destroy(&input_lock); 289 closelog(); 290 return -1; 291 } 292 initialize_curses(); 293 setup_windows(); 294 signal(SIGWINCH, resize_handler); 295 show_title_bar(); 296 show_sensors_w(); 297 show_cooling_device(); 298 update_thermal_data(); 299 show_data_w(); 300 prepare_logging(); 301 init_thermal_controller(); 302 303 nodelay(stdscr, TRUE); 304 err = pthread_create(&event_tid, NULL, &handle_tui_events, NULL); 305 if (err != 0) { 306 printf("\ncan't create thread :[%s]", strerror(err)); 307 tmon_cleanup(); 308 exit(EXIT_FAILURE); 309 } 310 311 /* validate range of user selected target zone, default to the first 312 * instance if out of range 313 */ 314 target_tz_index = zone_instance_to_index(target_thermal_zone); 315 if (target_tz_index < 0) { 316 target_thermal_zone = ptdata.tzi[0].instance; 317 syslog(LOG_ERR, "target zone is not found, default to %d\n", 318 target_thermal_zone); 319 } 320 while (1) { 321 sleep(ticktime); 322 show_title_bar(); 323 show_sensors_w(); 324 update_thermal_data(); 325 if (!dialogue_on) { 326 show_data_w(); 327 show_cooling_device(); 328 } 329 cur_thermal_record++; 330 time_elapsed += ticktime; 331 controller_handler(trec[0].temp[target_tz_index] / 1000, 332 &yk); 333 trec[0].pid_out_pct = yk; 334 if (!dialogue_on) 335 show_control_w(); 336 if (tmon_exit) 337 break; 338 } 339 tmon_cleanup(); 340 return 0; 341 } 342 343 static void start_daemon_mode() 344 { 345 daemon_mode = 1; 346 /* fork */ 347 pid_t sid, pid = fork(); 348 if (pid < 0) { 349 exit(EXIT_FAILURE); 350 } else if (pid > 0) 351 /* kill parent */ 352 exit(EXIT_SUCCESS); 353 354 /* disable TUI, it may not be necessary, but saves some resource */ 355 disable_tui(); 356 357 /* change the file mode mask */ 358 umask(S_IWGRP | S_IWOTH); 359 360 /* new SID for the daemon process */ 361 sid = setsid(); 362 if (sid < 0) 363 exit(EXIT_FAILURE); 364 365 /* change working directory */ 366 if ((chdir("/")) < 0) 367 exit(EXIT_FAILURE); 368 369 370 sleep(10); 371 372 close(STDIN_FILENO); 373 close(STDOUT_FILENO); 374 close(STDERR_FILENO); 375 376 } 377