#!/usr/bin/php /dev/null ".($debug ? "":"2>&1 ")."&"); syslog(LOG_INFO, "process started. To terminate it, type: $prog --quit"); exit(0); } ############################################# # ASRock # ipmi-raw 00 3a 01 00 00 00 00 00 00 00 00 # ipmi-raw 00 3a 01 AA BB CC DD EE FF GG HH # 00 = smartfan mode # 01 - 64 = 1% - 100% ############################################# ############################################# # ASRock 570 # ipmi-raw 00 3a d6 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # ipmi-raw 00 3a d6 AA BB CC DD EE FF GG HH II JJ KK LL MM NN OO PP # 00 = smartfan mode # 01 - 64 = 1% - 100% ############################################# ############################################# # ASRock Dual Socket # ipmi-raw 00 3a 01 CPU_1_OVERRIDE CPU_1 REAR_1 FRONT_1 FRONT_2 FRONT_3 # ipmi-raw 00 3a 11 CPU_2_OVERRIDE CPU_2 REAR_2 FRONT_4 # ipmi-raw 00 3a 01 00 AA BB CC DD EE # ipmi-raw 00 3a 11 00 AA BB CC ############################################# ############################################# # Supermicro X10/X11 # ipmi-raw 00 30 70 66 01 00 64 # ipmi-raw 00 30 70 66 AA BB CC # # AA # 00 - Get value # 01 - Set value # # BB # 00 - FAN 1/2/3/4 or CPU_FAN1/2 # 01 - FAN A or SYS_FAN1/2/3 # # CC # 00 to 64 - Set Speed (0-64) ############################################# ############################################# # Supermicro X9 # ipmi-raw 00 30 91 5A 3 00 FF # ipmi-raw 00 30 91 5A 3 BB CC # # BB # 10 - FAN 1/2/3/4 # 11 - FAN A # # CC # 00 to FF - Set Speed (0-255) ############################################# ############################## ###### FUNCTION SECTION ###### ############################## /* logger*/ function logger($msg, $quiet = false) { syslog(LOG_INFO, $msg); if (!$quiet) echo PHP_EOL.$msg.PHP_EOL; } /* logfile */ function fanlog($msg) { global $log; $msg = date('Y-m-d H:i:s').' '.$msg.PHP_EOL; file_put_contents($log,$msg,FILE_APPEND); } /* debug */ function debug($m){ global $prog, $debug; if($debug){ $STDERR = fopen('php://stderr', 'w+'); fwrite($STDERR, $m.PHP_EOL); fclose($STDERR); } } /* auto fan */ function autofan() { global $board_json, $board, $cmd_count, $fanopts; fanlog('Setting fans to auto'); if($board !== 'Supermicro'){ //if board is ASRock $cmd = ''; for($i = 0; $i <= $cmd_count; $i++){ $board0 = ($i == 0) ? $board : $board.$i; $raw = $board_json[$board0]['raw']; $auto = $board_json[$board0]['auto']; $cmd .= "ipmi-raw $raw d8 $auto $fanopts 2>&1 >/dev/null"; } }else{ //if board is Supermicro $raw = $board_json[$board]['raw']; $auto = $board_json[$board]['auto']; $mode = trim(shell_exec("ipmi-raw 00 30 45 00 | awk -F '45 00 ' '{print $2}'")); $cmd = "ipmi-raw $auto $mode $fanopts 2>&1 >/dev/null &"; } shell_exec($cmd); } /* full speed fan */ function fullfan() { global $board_json, $board, $cmd_count, $fanopts; fanlog('Setting fans to full speed'); if($board !== 'Supermicro'){ //if board is ASRock $cmd = ''; for($i = 0; $i <= $cmd_count; $i++){ $board0 = ($i == 0) ? $board : $board.$i; $raw = $board_json[$board0]['raw']; $full = $board_json[$board0]['full']; $cmd .= "ipmi-raw $raw d6 $full $fanopts 2>&1 >/dev/null &"; } }else{ //if board is Supermicro $full = $board_json[$board]['full']; $cmd = "ipmi-raw $full $fanopts 2>&1 >/dev/null &"; } shell_exec($cmd); sleep(10); } /* get highest temp of hard drives */ function get_highest_temp(){ global $hddignore; $ignore = array_flip(explode(',', $hddignore)); //get UA devices $ua_json = '/var/state/unassigned.devices/hdd_temp.json'; $ua_devs = file_exists($ua_json) ? json_decode(file_get_contents($ua_json), true) : []; //get all hard drives $hdds = array_merge(parse_ini_file('/var/local/emhttp/disks.ini',true), parse_ini_file('/var/local/emhttp/devs.ini',true)); unset($hdds['flash']); $highest_temp = 0; foreach ($hdds as $hdd) { if (!array_key_exists($hdd['id'], $ignore)) { $temp = 0; if(array_key_exists('temp', $hdd)) $temp = $hdd['temp']; elseif(!empty($ua_devs)){ $ua_key = "/dev/".$hdd['device']; $temp = (array_key_exists($ua_key, $ua_devs)) ? $ua_devs[$ua_key]['temp'] : 0; }else $temp = preg_replace("/\s+/", "", shell_exec("smartctl -A -n standby /dev/${hdd} 2>/dev/null| grep -m 1 -i Temperature_Cel | awk '{print $10}'")); $highest_temp = ($temp > $highest_temp) ? $temp : $highest_temp; } } debug("Highest temp is ${highest_temp}ºC"); return $highest_temp; } /* get fan and temp sensors */ function ipmi_fan_sensors() { global $ipmi, $fanopts, $hdd_temp; // return empty array if no ipmi detected or network options if(!$ipmi && empty($fanopts)) return []; $cmd = "/usr/sbin/ipmi-sensors --comma-separated-output --no-header-output --interpret-oem-data $fanopts 2>/dev/null"; exec($cmd, $output, $return_var=null); // return empty array if error if($return_var) return []; // add hard drive temp as a sensor $output[] = "99,HDD Temperature,Temperature, $hdd_temp,C,Ok"; // key names for ipmi sensors output $keys = ['ID', 'Name', 'Type', 'Reading', 'Units', 'Event']; $sensors = []; foreach($output as $line){ /// add sensor keys as keys to ipmi sensor output $sensor_raw = explode(",", $line); $size_raw = sizeof($sensor_raw); $sensor = ($size_raw < 6) ? []: array_combine($keys, array_slice($sensor_raw,0,6,true)); if ($sensor['Type'] === 'Temperature' || $sensor['Type'] === 'Fan') $sensors[$sensor['ID']] = $sensor; } return $sensors; // sensor readings unset($sensors); } ############################## ##### PROGRAM SECTION ###### ############################## $MD5 = md5_file($fancfg_file); $hdd_temp = get_highest_temp(); $sensors = ipmi_fan_sensors(); $ipmipoll = ($fanpoll <= $hddpoll) ? $fanpoll : $hddpoll; $fan_count = $fanpoll * 10; $hdd_count = $hddpoll * 10; $curent_hex = ''; $current_hex2 = ''; $manual_cmd = "ipmi-raw 00 3a d8 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 2>&1 >/dev/null;"; fanlog('Starting Fan Control'); if($board == 'Supermicro'){ fullfan(); } while(TRUE){ while(TRUE){ #### DO YOUR STUFF HERE #### /* Refresh hdds and hdd temp if hdd poll time is expired */ if ($hdd_count <= 0) { $hdd_count = $hddpoll * 10; $hdd_temp = get_highest_temp(); $sensors['99']['Reading'] = $hdd_temp; } /* Get sensor info if fan sensor poll time is expired */ if ($fan_count <= 0) { $fan_count = $fanpoll * 10; $sensors = ipmi_fan_sensors(); } for($i = 0; $i <= $cmd_count; $i++){ $board0 = ($i == 0) ? $board : $board.$i; $raw = $board_json[$board0]['raw']; $fans = $board_json[$board0]['fans']; $hex = ' d6'; $msg = 'Fan:Temp'; $cmd = ''; $exec = false; $fan_count = 0; foreach($fans as $fan => $value){ $temp = isset($fancfg["TEMP_$fan"]) ? $fancfg["TEMP_$fan"] : ''; if(!empty($temp)) { $templo = (isset($fancfg["TEMPLO_$fan"])) ? intval($fancfg["TEMPLO_$fan"]) : 30; $temphi = (isset($fancfg["TEMPHI_$fan"])) ? intval($fancfg["TEMPHI_$fan"]) : 45; $fanmin = (isset($fancfg["FANMIN_$fan"])) ? intval($fancfg["FANMIN_$fan"]) : 16; $fanmax = (isset($fancfg["FANMAX_$fan"])) ? intval($fancfg["FANMAX_$fan"]) : $range; $reading = floatval($sensors[$temp]['Reading']); $name = htmlspecialchars($sensors[$temp]['Name']); if ($reading <= $templo){ $pct = 0; $pwm = 1; }elseif ($reading >= $temphi){ $pct = 1; $pwm = $range; }else{ $pct = ($reading-$templo)/($temphi-$templo)*($fanmax-$fanmin)/$range + $fanmin/$range; $pwm = round(($pct)*($range/4))*4; //round } //impose fan lower limit if ($pwm <= $fanmin){ $pwm = $fanmin; $pct = $fanmin/$range; } //impose fan upper limit if ($pwm >= $fanmax){ $pwm = $fanmax; $pct = $fanmax/$range; } //convert pwm to hexadecimal if($range == 255){ $pwm = dechex($pwm); } //pad pwm to 2 places $pwm = str_pad($pwm, 2, '0', STR_PAD_LEFT); //add fan, value, temp sensor name and reading $pct = str_pad(round($pct*100), 2, ' ', STR_PAD_LEFT); $msg .= ", ${fan}(${pct}%):".str_replace('Temperature','Temp',$name)."(${reading}°C)"; }else{ $pwm ='14'; } if($board !== 'Supermicro'){ //add value to hex for Asrock boards if($cmd_count !== 0 && $fan_count == 0){ // set value for CPU fans for dual sockets if($pwm == '00'){ $hex .= ' 00 00'; }else{ $hex .= " d6 $pwm"; } }else{ $hex .= " $pwm"; } }else{ //create command for Supermicro $cmd_str = "ipmi-raw $raw $value $pwm $fanopts 2>&1; "; //check if new pwm for FAN1234 then add to cmd string if(($value == '00' || $value == '10') && $current_pwm !== $pwm && $pwm !== '00'){ $cmd .= $cmd_str; //set current value for next loop $current_pwm = $pwm; $exec = true; } // check if new pwm for FANA then add to cmd string if(($value == '01' || $value == '11') && $current_pwm2 !== $pwm && $pwm !== '00'){ $cmd .= $cmd_str; //set current value for next loop $current_pwm2 = $pwm; $exec = true; } } $fan_count++; } if($board !== 'Supermicro'){ //compare last value to new value for Asrock boards $cmd_str = "ipmi-raw $raw$hex $fanopts 2>&1"; if($i == 0){ if($current_hex !== $hex){ $cmd = $cmd_str; //set current value for next loop $current_hex = $hex; $exec = true; } }else{ if($current_hex2 !== $hex){ $cmd = $cmd_str; //set 2nd current value for next loop $current_hex2 = $hex; $exec = true; } } } // execute command if exec set if($exec){ shell_exec($manual_cmd); shell_exec($cmd); if($debug) fanlog($cmd); //log changes fanlog($msg); } } /* print variable */ $defined_vars = get_defined_vars(); foreach (array('_GET','_POST','_COOKIE','_FILES','argv','argc','_SERVER') as $i) unset($defined_vars[$i]); if($debug) debug("\nDECLARED VARIABLES:\n".print_r($defined_vars, true)); unset($defined_vars); $time1 = time(); for ($i=0; $i < $ipmipoll ; $i++) { sleep(10); $fan_count = $fan_count - 10; $hdd_count = $hdd_count - 10; $MD5_new = md5_file($fancfg_file); if((file_exists($fancfg_file)) && ($MD5_new != $MD5)){ $msg = 'fan control config file updated, reloading settings'; $fancfg = parse_ini_file($fancfg_file); logger($msg, $argq); $MD5 = $MD5_new; fanlog($msg); break; } } debug("Sleep ".(time()-$time1)." seconds."); ###### END OF SECTION ###### }; }; ?>