From afebc8dc39c61466245e97b11d23b2254d27ddf8 Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Sun, 3 Aug 2025 07:44:11 +1000 Subject: [PATCH] systeminfo refactor #1124 --- back/app.conf | 2 +- docker-compose.yml | 3 + front/deviceDetails.php | 2 +- front/systeminfo.php | 770 ++++++------------------------------ front/systeminfoNetwork.php | 279 +++++++++++++ front/systeminfoServer.php | 327 +++++++++++++++ front/systeminfoStorage.php | 124 ++++++ server/initialise.py | 2 +- 8 files changed, 866 insertions(+), 643 deletions(-) create mode 100755 front/systeminfoNetwork.php create mode 100755 front/systeminfoServer.php create mode 100755 front/systeminfoStorage.php diff --git a/back/app.conf b/back/app.conf index 4507083b..469c4e8e 100755 --- a/back/app.conf +++ b/back/app.conf @@ -24,7 +24,7 @@ LOADED_PLUGINS=['ARPSCAN', 'AVAHISCAN', 'CSVBCKP','DBCLNP', 'DIGSCAN', 'INTRNT', DAYS_TO_KEEP_EVENTS=90 # Used for generating links in emails. Make sure not to add a trailing slash! -REPORT_DASHBOARD_URL='http://127.0.0.1' +REPORT_DASHBOARD_URL='update_REPORT_DASHBOARD_URL_setting' # Make sure at least these scanners are enabled for new installs, other defaults are taken from the config.json INTRNT_RUN='schedule' diff --git a/docker-compose.yml b/docker-compose.yml index 0688911c..a6a37418 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,6 +59,9 @@ services: - ${DEV_LOCATION}/front/presence.php:/app/front/presence.php - ${DEV_LOCATION}/front/settings.php:/app/front/settings.php - ${DEV_LOCATION}/front/systeminfo.php:/app/front/systeminfo.php + - ${DEV_LOCATION}/front/systeminfoNetwork.php:/app/front/systeminfoNetwork.php + - ${DEV_LOCATION}/front/systeminfoServer.php:/app/front/systeminfoServer.php + - ${DEV_LOCATION}/front/systeminfoStorage.php:/app/front/systeminfoStorage.php - ${DEV_LOCATION}/front/cloud_services.php:/app/front/cloud_services.php - ${DEV_LOCATION}/front/report.php:/app/front/report.php - ${DEV_LOCATION}/front/workflows.php:/app/front/workflows.php diff --git a/front/deviceDetails.php b/front/deviceDetails.php index e996cd4d..68ce85f0 100755 --- a/front/deviceDetails.php +++ b/front/deviceDetails.php @@ -541,7 +541,7 @@ function updateDevicePageName(mac) { window.onload = function async() { - initializeTabs(); + // initializeTabs(); updateChevrons(mac); updateDevicePageName(mac); diff --git a/front/systeminfo.php b/front/systeminfo.php index 09996195..170b3b82 100755 --- a/front/systeminfo.php +++ b/front/systeminfo.php @@ -21,6 +21,8 @@ // show spinning icon showSpinner() + + var selectedTab = 'tabServer'; @@ -28,558 +30,67 @@
+
+
+ + -
+ '; - - diff --git a/front/systeminfoNetwork.php b/front/systeminfoNetwork.php new file mode 100755 index 00000000..cf0cf5c9 --- /dev/null +++ b/front/systeminfoNetwork.php @@ -0,0 +1,279 @@ + + + + +
+

' . lang('Systeminfo_Network_Hardware') . '

+
+
+ + + + + + + + + + '; + +for ($x = 0; $x < sizeof($net_interfaces); $x++) { + $interface_name = str_replace(':', '', $net_interfaces[$x]); + $interface_ip_temp = exec('ip addr show ' . $interface_name . ' | grep "inet "'); + $interface_ip_arr = explode(' ', trim($interface_ip_temp)); + + if (!isset($interface_ip_arr[1])) { + $interface_ip_arr[1] = '--'; + } + + if ($net_interfaces_rx[$x] == 0) { + $temp_rx = 0; + } else { + $temp_rx = number_format(round(($net_interfaces_rx[$x] / 1024 / 1024), 2), 2, ',', '.'); + } + if ($net_interfaces_tx[$x] == 0) { + $temp_tx = 0; + } else { + $temp_tx = number_format(round(($net_interfaces_tx[$x] / 1024 / 1024), 2), 2, ',', '.'); + } + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; +} + +echo ' +
' . lang('Systeminfo_Network_Hardware_Interface_Name') . '' . lang('Systeminfo_Network_Hardware_Interface_Mask') . '' . lang('Systeminfo_Network_Hardware_Interface_RX') . '' . lang('Systeminfo_Network_Hardware_Interface_TX') . '
' . $interface_name . '' . $interface_ip_arr[1] . '' . $temp_rx . ' MB' . $temp_tx . ' MB
+
+ '; + +// Available IPs ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_AvailableIps') . '

+
+
+ +
+
'; + + + +// Network ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_Network') . '

+
+
+
+
' . lang('Systeminfo_Network_IP') . '
+
' . shell_exec("curl https://ifconfig.co") . '
+
+
+
' . lang('Systeminfo_Network_IP_Connection') . '
+
' . $_SERVER['REMOTE_ADDR'] . '
+
+
+
' . lang('Systeminfo_Network_IP_Server') . '
+
' . $_SERVER['SERVER_ADDR'] . '
+
+
+
' . lang('Systeminfo_Network_Server_Name') . '
+
' . $network_NAME . '
+
+
+
' . lang('Systeminfo_Network_Connection_Port') . '
+
' . $_SERVER['REMOTE_PORT'] . '
+
+
+
' . lang('Systeminfo_Network_Secure_Connection') . '
+
' . $network_HTTPS . '
+
+
+
' . lang('Systeminfo_Network_Server_Version') . '
+
' . $_SERVER['SERVER_SOFTWARE'] . '
+
+
+
' . lang('Systeminfo_Network_Request_URI') . '
+
' . $_SERVER['REQUEST_URI'] . '
+
+
+
' . lang('Systeminfo_Network_Server_Query') . '
+
' . $network_QueryString . '
+
+
+
' . lang('Systeminfo_Network_HTTP_Host') . '
+
' . $_SERVER['HTTP_HOST'] . '
+
+
+
' . lang('Systeminfo_Network_HTTP_Referer') . '
+
' . $network_referer . '
+
+
+
' . lang('Systeminfo_Network_MIME') . '
+
' . $_SERVER['HTTP_ACCEPT'] . '
+
+
+
' . lang('Systeminfo_Network_Accept_Language') . '
+
' . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . '
+
+
+
' . lang('Systeminfo_Network_Accept_Encoding') . '
+
' . $_SERVER['HTTP_ACCEPT_ENCODING'] . '
+
+
+
' . lang('Systeminfo_Network_Request_Method') . '
+
' . $_SERVER['REQUEST_METHOD'] . '
+
+
+
' . lang('Systeminfo_Network_Request_Time') . '
+
' . $_SERVER['REQUEST_TIME'] . '
+
+
+
'; + + +?> + + + + \ No newline at end of file diff --git a/front/systeminfoServer.php b/front/systeminfoServer.php new file mode 100755 index 00000000..72729885 --- /dev/null +++ b/front/systeminfoServer.php @@ -0,0 +1,327 @@ + + +format('l, F j, Y H:i:s'); // Get date +$formatted_date2 = $date->format('d/m/Y H:i:s'); // Get date2 +$formatted_date3 = $date->format('Y/m/d H:i:s'); // Get date3 +//System stats +// OS-Version +$os_version = ''; +// Raspbian +if ($os_version == '') {$os_version = exec('cat /etc/os-release | grep PRETTY_NAME');} +// Dietpi +if ($os_version == '') {$os_version = exec('uname -o');} +//$os_version_arr = explode("\n", trim($os_version)); +$stat['os_version'] = str_replace('"', '', str_replace('PRETTY_NAME=', '', $os_version)); +$stat['uptime'] = str_replace('up ', '', shell_exec("uptime -p")); // Get system uptime +$system_namekernel = shell_exec("uname"); // Get system name kernel +$system_namesystem = shell_exec("uname -o"); // Get name system +$system_full = shell_exec("uname -a"); // Get system full +$system_architecture = shell_exec("uname -m"); // Get system Architecture +$load_average = sys_getloadavg(); // Get load average +$system_process_count = shell_exec("ps -e --no-headers | wc -l"); // Count processes +//Motherboard stats +$motherboard_name = shell_exec('cat /sys/class/dmi/id/board_name'); // Get the Motherboard name +$motherboard_manufactured = shell_exec('cat /sys/class/dmi/id/board_vendor'); // Get the Motherboard manufactured +$motherboard_revision = shell_exec('cat /sys/class/dmi/id/board_version'); // Get the Motherboard revision +$motherboard_bios = shell_exec('cat /sys/class/dmi/id/bios_version'); // Get the Motherboard BIOS +$motherboard_biosdate = shell_exec('cat /sys/class/dmi/id/bios_date'); // Get the Motherboard BIOS date +$motherboard_biosvendor = shell_exec('cat /sys/class/dmi/id/bios_vendor'); // Get the Motherboard BIOS vendor +//CPU stats +$prevVal = shell_exec("cat /proc/cpuinfo | grep processor"); +$prevArr = explode("\n", trim($prevVal)); +$stat['cpu'] = sizeof($prevArr); +$cpu_result = shell_exec("cat /proc/cpuinfo | grep Model"); +$stat['cpu_model'] = strstr($cpu_result, "\n", true); +$stat['cpu_model'] = str_replace(":", "", trim(str_replace("Model", "", $stat['cpu_model']))); +if ($stat['cpu_model'] == '') { + $cpu_result = shell_exec("cat /proc/cpuinfo | grep model\ name"); + $stat['cpu_model'] = strstr($cpu_result, "\n", true); + $stat['cpu_model'] = str_replace(":", "", trim(str_replace("model name", "", $stat['cpu_model']))); +} +if (file_exists('/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq')) { + // RaspbianOS + $stat['cpu_frequ'] = exec('cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq') / 1000; +} elseif (is_numeric(str_replace(',', '.', exec('lscpu | grep "MHz" | awk \'{print $3}\'')))) { + // Ubuntu Server, DietPi event. others + $stat['cpu_frequ'] = round(exec('lscpu | grep "MHz" | awk \'{print $3}\''), 0); +} elseif (is_numeric(str_replace(',', '.', exec('lscpu | grep "max MHz" | awk \'{print $4}\'')))) { + // RaspbianOS and event. others + $stat['cpu_frequ'] = round(str_replace(',', '.', exec('lscpu | grep "max MHz" | awk \'{print $4}\'')), 0); +} else { + // Fallback + $stat['cpu_frequ'] = "unknown"; +} +$cpu_temp = shell_exec('cat /sys/class/hwmon/hwmon0/temp1_input'); // Get the CPU temperature +$cpu_temp = floatval($cpu_temp) / 1000; // Convert the temperature to degrees Celsius +$cpu_vendor = exec('cat /proc/cpuinfo | grep "vendor_id" | cut -d ":" -f 2' ); // Get the CPU vendor +//Memory stats +$total_memorykb = shell_exec("cat /proc/meminfo | grep MemTotal | awk '{print $2}'"); +$total_memorykb = trim($total_memorykb); +$total_memorykb = number_format($total_memorykb, 0, '.', '.'); +$total_memorymb = shell_exec("cat /proc/meminfo | grep MemTotal | awk '{print $2/1024}'"); +$total_memorymb = trim($total_memorymb); +$total_memorymb = number_format($total_memorymb, 0, '.', '.'); +$mem_used = round(memory_get_usage() / 1048576 * 100, 2); +$memory_usage_percent = round(($mem_used / $total_memorymb), 2); + + + +// General ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_General') . '

+
+
+
+
' . lang('Systeminfo_General_Full_Date') . '
+
' . $formatted_date . '
+
+
+
' . lang('Systeminfo_General_Date') . '
+
' . $formatted_date2 . '
+
+
+
' . lang('Systeminfo_General_Date2') . '
+
' . $formatted_date3 . '
+
+
+
' . lang('Systeminfo_General_TimeZone') . '
+
' . $timeZone . '
+
+
+
'; + + +// Client ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_This_Client') . '

+
+
+
+
' . lang('Systeminfo_Client_User_Agent') . '
+
' . $_SERVER['HTTP_USER_AGENT'] . '
+
+
+
' . lang('Systeminfo_Client_Resolution') . '
+
+
+
+
'; + +echo ''; + +// System ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_System') . '

+
+
+
+
' . lang('Systeminfo_System_Uptime') . '
+
' . $stat['uptime'] . '
+
+
+
' . lang('Systeminfo_System_Kernel') . '
+
' . $system_namekernel . '
+
+
+
' . lang('Systeminfo_System_System') . '
+
' . $system_namesystem . '
+
+
+
' . lang('Systeminfo_System_OSVersion') . '
+
' . $stat['os_version'] . '
+
+
+
' . lang('Systeminfo_System_Uname') . '
+
' . $system_full . '
+
+
+
' . lang('Systeminfo_System_Architecture') . '
+
' . $system_architecture . '
+
+
+
' . lang('Systeminfo_System_AVG') . '
+
'. $load_average[0] .' '. $load_average[1] .' '. $load_average[2] .'
+
+
+
' . lang('Systeminfo_System_Running_Processes') . '
+
' . $system_process_count . '
+
+
+
'; + +// Motherboard ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_Motherboard') . '

+
+
+
+
' . lang('Systeminfo_Motherboard_Name') . '
+
' . $motherboard_name . '
+
+
+
' . lang('Systeminfo_Motherboard_Manufactured') . '
+
' . $motherboard_manufactured . '
+
+
+
' . lang('Systeminfo_Motherboard_Revision') . '
+
' . $motherboard_revision. '
+
+
+
' . lang('Systeminfo_Motherboard_BIOS') . '
+
' . $motherboard_bios . '
+
+
+
' . lang('Systeminfo_Motherboard_BIOS_Date') . '
+
' . $motherboard_biosdate . '
+
+
+
' . lang('Systeminfo_Motherboard_BIOS_Vendor') . '
+
' . $motherboard_biosvendor . '
+
+
+
'; + +// CPU ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_CPU') . '

+
+
+
+
' . lang('Systeminfo_CPU_Vendor') . '
+
' . $cpu_vendor . '
+
+
+
' . lang('Systeminfo_CPU_Name') . '
+
' . $stat['cpu_model'] . '
+
+
+
' . lang('Systeminfo_CPU_Cores') . '
+
' . $stat['cpu'] . '
+
+
+
' . lang('Systeminfo_CPU_Speed') . '
+
' . $stat['cpu_frequ'] . ' MHz
+
+
+
' . lang('Systeminfo_CPU_Temp') . '
+
'. $cpu_temp .' °C
+
'; + // Get the number of CPU cores + $num_cpus = $stat['cpu']; + $num_cpus = $num_cpus +2; + + // Iterate over the CPU cores + for ($i = 2,$a = 0; $i < $num_cpus; $i++,$a++) { + + // Get the CPU temperature + $cpu_tempxx = shell_exec('cat /sys/class/hwmon/hwmon0/temp' . $i . '_input'); + + // Convert the temperature to degrees Celsius + $cpu_tempxx = floatval($cpu_tempxx) / 1000; + + // Print the CPU temperature + echo '
+
CPU Temp ' . $a . ':
+
' . $cpu_tempxx . ' °C
+
'; + } + echo ' +
+
'; + +// Memory ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_Memory') . '

+
+
+
+
' . lang('Systeminfo_Memory_Usage_Percent') . '
+
' . $memory_usage_percent . ' %
+
+
+
' . lang('Systeminfo_Memory_Usage') . '
+
' . $mem_used . ' MB / ' . $total_memorymb . ' MB
+
+
+
' . lang('Systeminfo_Memory_Total_Memory') . '
+
' . $total_memorymb . ' MB (' . $total_memorykb . ' KB)
+
+
+
'; + + + +// Services ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_Services') . '

+
+
'; +echo '
'; + exec('systemctl --type=service --state=running', $running_services); +echo ''; + echo ' + + + + + '; +$table_color = 'odd'; +for ($x = 0; $x < sizeof($running_services); $x++) { + if (stristr($running_services[$x], '.service')) { + $temp_services_arr = array_values(array_filter(explode(' ', trim($running_services[$x])))); + $servives_name = $temp_services_arr[0]; + unset($temp_services_arr[0], $temp_services_arr[1], $temp_services_arr[2], $temp_services_arr[3]); + $servives_description = implode(" ", $temp_services_arr); + + if ($table_color == 'odd') + { + $table_color = 'even'; + } else + { + $table_color = 'odd'; + } + echo ''; + } +} + echo ''; + echo '
'; +echo '
+
'; + + +?> + + \ No newline at end of file diff --git a/front/systeminfoStorage.php b/front/systeminfoStorage.php new file mode 100755 index 00000000..2211197c --- /dev/null +++ b/front/systeminfoStorage.php @@ -0,0 +1,124 @@ + + + + + +
+

' . lang('Systeminfo_Storage') . '

+
+
'; + +$storage_lsblk = shell_exec("lsblk -io NAME,SIZE,TYPE,MOUNTPOINT,MODEL --list | tail -n +2 | awk '{print $1\"#\"$2\"#\"$3\"#\"$4\"#\"$5}'"); +$storage_lsblk_line = explode("\n", $storage_lsblk); +$storage_lsblk_line = array_filter($storage_lsblk_line); + +for ($x = 0; $x < sizeof($storage_lsblk_line); $x++) { + $temp = array(); + $temp = explode("#", $storage_lsblk_line[$x]); + $storage_lsblk_line[$x] = $temp; +} + +for ($x = 0; $x < sizeof($storage_lsblk_line); $x++) { + echo '
'; + if (preg_match('~[0-9]+~', $storage_lsblk_line[$x][0])) { + echo '
"' . lang('Systeminfo_Storage_Mount') . ' ' . $storage_lsblk_line[$x][3] . '"
'; + } else { + echo '
"' . str_replace('_', ' ', $storage_lsblk_line[$x][3]) . '"
'; + } + echo '
' . lang('Systeminfo_Storage_Device') . ' /dev/' . $storage_lsblk_line[$x][0] . '
'; + echo '
' . lang('Systeminfo_Storage_Size') . ' ' . $storage_lsblk_line[$x][1] . '
'; + echo '
' . lang('Systeminfo_Storage_Type') . ' ' . $storage_lsblk_line[$x][2] . '
'; + echo '
'; +} +echo '
+ '; + +// Storage usage ---------------------------------------------------------- +echo '
+
+

' . lang('Systeminfo_Storage_Usage') . '

+
+
'; +for ($x = 0; $x < sizeof($hdd_devices); $x++) { + if (stristr($hdd_devices[$x], '/dev/')) { + if (!stristr($hdd_devices[$x], '/loop')) { + if ($hdd_devices_total[$x] == 0 || $hdd_devices_total[$x] == '') {$temp_total = 0;} else { $temp_total = number_format(round(($hdd_devices_total[$x] / 1024 / 1024), 2), 2, ',', '.'); $temp_total = trim($temp_total);} + if ($hdd_devices_used[$x] == 0 || $hdd_devices_used[$x] == '') {$temp_used = 0;} else { $temp_used = number_format(round(($hdd_devices_used[$x] / 1024 / 1024), 2), 2, ',', '.'); $temp_used = trim($temp_total);} + if ($hdd_devices_free[$x] == 0 || $hdd_devices_free[$x] == '') {$temp_free = 0;} else { $temp_free = number_format(round(($hdd_devices_free[$x] / 1024 / 1024), 2), 2, ',', '.'); $temp_free = trim($temp_total);} + echo '
'; + echo '
"' . lang('Systeminfo_Storage_Usage_Mount') . ' ' . $hdd_devices_mount[$x] . '"
'; + echo '
' . lang('Systeminfo_Storage_Usage_Total') . ' ' . $temp_total . ' GB
'; + echo '
' . lang('Systeminfo_Storage_Usage_Used') . ' ' . $temp_used . ' GB (' . $hdd_devices_percent[$x]. ')
'; + echo '
' . lang('Systeminfo_Storage_Usage_Free') . ' ' . $temp_free . ' GB
'; + echo '
'; + } + } +} +#echo '
' . $lang['SysInfo_storage_note']; +echo '
+
'; + +// ---------------------------------------------------------- +// USB devices +// ---------------------------------------------------------- + +$usb_result = shell_exec("lsusb"); +$usb_devices_mount = explode("\n", trim($usb_result)); + +echo '
+
+

' . lang('Systeminfo_USB_Devices') . '

+
+
'; +echo ' '; + +$table_color = 'odd'; +sort($usb_devices_mount); +for ($x = 0; $x < sizeof($usb_devices_mount); $x++) { + $cut_pos = strpos($usb_devices_mount[$x], ':'); + $usb_bus = substr($usb_devices_mount[$x], 0, $cut_pos); + $usb_dev = substr($usb_devices_mount[$x], $cut_pos + 1); + + if ($table_color == 'odd') {$table_color = 'even';} else { $table_color = 'odd';} + echo ''; +} +echo ' '; +echo '
+
'; + +// ---------------------------------------------------------- + +echo '
'; + +?> + + \ No newline at end of file diff --git a/server/initialise.py b/server/initialise.py index 3a321f02..3df50575 100755 --- a/server/initialise.py +++ b/server/initialise.py @@ -163,7 +163,7 @@ def importConfigs (db, all_plugins): conf.LOG_LEVEL = ccd('LOG_LEVEL', 'verbose' , c_d, 'Log verboseness', '{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}', "['none', 'minimal', 'verbose', 'debug', 'trace']", 'General') conf.TIMEZONE = ccd('TIMEZONE', default_tz , c_d, 'Time zone', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General') conf.PLUGINS_KEEP_HIST = ccd('PLUGINS_KEEP_HIST', 250 , c_d, 'Keep history entries', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', '[]', 'General') - conf.REPORT_DASHBOARD_URL = ccd('REPORT_DASHBOARD_URL', 'http://127.0.0.1/' , c_d, 'NetAlertX URL', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General') + conf.REPORT_DASHBOARD_URL = ccd('REPORT_DASHBOARD_URL', 'update_REPORT_DASHBOARD_URL_setting' , c_d, 'NetAlertX URL', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General') conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', '[]', 'General') conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General') conf.HRS_TO_KEEP_OFFDEV = ccd('HRS_TO_KEEP_OFFDEV', 0 , c_d, 'Keep offline devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')