/* * * Conky, a system monitor, based on torsmo * * Any original torsmo code is licensed under the BSD license * * All code written since the fork of torsmo is licensed under the GPL * * Please see COPYING for details * * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al. * (see AUTHORS) * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "conky.h" #include "diskio.h" #include "top.h" #include #include "net_stat.h" #include "solaris.h" static kstat_ctl_t *kstat; static time_t kstat_updated; static int pageshift = INT_MAX; static pthread_mutex_t kstat_mtx = PTHREAD_MUTEX_INITIALIZER; static int pagetok(int pages) { if (pageshift == INT_MAX) { int pagesize = sysconf(_SC_PAGESIZE); pageshift = 0; while ((pagesize >>= 1) > 0) pageshift++; pageshift -= 10; /* 2^10 = 1024 */ } return (pageshift > 0 ? pages << pageshift : pages >> -pageshift); } static void update_kstat() { time_t now; pthread_mutex_lock(&kstat_mtx); now = time(nullptr); if (kstat == nullptr) { if ((kstat = kstat_open()) == nullptr) { pthread_mutex_unlock(&kstat_mtx); NORM_ERR("can't open kstat: %s", strerror(errno)); return; } kstat_updated = 0; } if (now - kstat_updated < 2) { /* Do not update kstats too often */ pthread_mutex_unlock(&kstat_mtx); return; } if (kstat_chain_update(kstat) == -1) { pthread_mutex_unlock(&kstat_mtx); perror("kstat_chain_update"); return; } kstat_updated = now; pthread_mutex_unlock(&kstat_mtx); } static kstat_named_t *get_kstat(const char *module, int inst, const char *name, const char *stat) { kstat_t *ksp; update_kstat(); pthread_mutex_lock(&kstat_mtx); ksp = kstat_lookup(kstat, (char *)module, inst, (char *)name); if (ksp == nullptr) { NORM_ERR("cannot lookup kstat %s:%d:%s:%s %s", module, inst, name, stat, strerror(errno)); pthread_mutex_unlock(&kstat_mtx); return nullptr; } if (kstat_read(kstat, ksp, nullptr) >= 0) { if (ksp->ks_type == KSTAT_TYPE_NAMED || ksp->ks_type == KSTAT_TYPE_TIMER) { kstat_named_t *knp = (kstat_named_t *)kstat_data_lookup(ksp, (char *)stat); pthread_mutex_unlock(&kstat_mtx); return knp; } else { NORM_ERR("kstat %s:%d:%s:%s has unexpected type %d", module, inst, name, stat, ksp->ks_type); pthread_mutex_unlock(&kstat_mtx); return nullptr; } } NORM_ERR("cannot read kstat %s:%d:%s:%s", module, inst, name, stat); pthread_mutex_unlock(&kstat_mtx); return nullptr; } void prepare_update() { kstat_updated = 0; } int update_meminfo() { kstat_named_t *knp; int nswap = swapctl(SC_GETNSWP, 0); struct swaptable *swt; struct swapent *swe; char path[PATH_MAX]; unsigned long stp, sfp; /* RAM stats */ knp = get_kstat("unix", -1, "system_pages", "freemem"); if (knp != nullptr) info.memfree = pagetok(knp->value.ui32); info.memmax = pagetok(sysconf(_SC_PHYS_PAGES)); if (info.memmax > info.memfree) info.mem = info.memmax - info.memfree; else /* for non-global zones with capped memory */ info.mem = info.memmax; /* Swap stats */ if (nswap < 1) return 0; /* for swapctl(2) */ swt = (struct swaptable *)malloc(nswap * sizeof(struct swapent) + sizeof(int)); if (swt == nullptr) return 0; swt->swt_n = nswap; swe = &(swt->swt_ent[0]); /* We are not interested in ste_path */ for (int i = 0; i < nswap; i++) swe[i].ste_path = path; nswap = swapctl(SC_LIST, swt); swe = &(swt->swt_ent[0]); stp = sfp = 0; for (int i = 0; i < nswap; i++) { if ((swe[i].ste_flags & ST_INDEL) || (swe[i].ste_flags & ST_DOINGDEL)) continue; stp += swe->ste_pages; sfp += swe->ste_free; } free(swt); info.swapfree = pagetok(sfp); info.swapmax = pagetok(stp); info.swap = info.swapmax - info.swapfree; return 0; } int check_mount(struct text_object *obj) { /* stub */ (void)obj; return 0; } double get_battery_perct_bar(struct text_object *obj) { /* Not implemented */ (void)obj; return 100.0; } double get_acpi_temperature(int fd) { /* Not implemented */ (void)fd; return 0.0; } int update_total_processes(void) { kstat_named_t *knp = get_kstat("unix", -1, "system_misc", "nproc"); if (knp != nullptr) info.procs = knp->value.ui32; return 0; } void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item) { /* Not implemented */ } int update_running_processes(void) { /* There is no kstat for this, see update_proc_entry() */ return 0; } int update_net_stats(void) { struct ifconf ifc; int sockfd; char buf[1024]; double d = current_update_time - last_update_time; if (d < 0.1) return 0; /* Find all active net interfaces */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { NORM_ERR("cannot create socket: %s", strerror(errno)); return 0; } ifc.ifc_buf = buf; ifc.ifc_len = sizeof(buf); if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { NORM_ERR("ioctl(SIOCGIFCONF) failed: %s", strerror(errno)); (void)close(sockfd); return 0; } (void)close(sockfd); /* Collect stats for all active interfaces */ for (int i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++) { struct net_stat *ns; struct ifreq *ifr = &ifc.ifc_req[i]; long long last_recv, last_trans; long long r, t; kstat_named_t *knp; ns = get_net_stat((const char *)ifr->ifr_name, nullptr, NULL); ns->up = 1; memcpy(&(ns->addr), &ifr->ifr_addr, sizeof(ifr->ifr_addr)); /* Skip the loopback interface, it does not have kstat data */ if (ifr->ifr_flags & IFF_LOOPBACK || strcmp(ifr->ifr_name, "lo0") == 0) continue; last_recv = ns->recv; last_trans = ns->trans; /* Get received bytes */ knp = get_kstat("link", -1, ifr->ifr_name, "rbytes"); if (knp == nullptr) { NORM_ERR("cannot read rbytes for %s\n", ifr->ifr_name); continue; } r = (long long)knp->value.ui32; if (r <= ns->last_read_recv) { ns->recv += ((long long)4294967295U - ns->last_read_recv) + r; } else { ns->recv += (r - ns->last_read_recv); } ns->last_read_recv = r; /* Get transceived bytes */ knp = get_kstat("link", -1, ifr->ifr_name, "obytes"); if (knp == nullptr) { NORM_ERR("cannot read obytes for %s\n", ifr->ifr_name); continue; } t = (long long)knp->value.ui32; if (t < ns->last_read_trans) { ns->trans += ((long long)4294967295U - ns->last_read_trans) + t; } else { ns->trans += (t - ns->last_read_trans); } ns->last_read_trans = t; ns->recv_speed = (ns->recv - last_recv) / d; ns->trans_speed = (ns->trans - last_trans) / d; } return 0; } int update_cpu_usage(void) { static int last_cpu_cnt = 0; static int *last_cpu_use = nullptr; double d = current_update_time - last_update_time; double total_cpu_usage = 0; int cpu; if (d < 0.1) return 0; update_kstat(); info.cpu_count = sysconf(_SC_NPROCESSORS_ONLN); /* (Re)allocate the array with previous values */ if (last_cpu_cnt != info.cpu_count || last_cpu_use == nullptr) { last_cpu_use = (int *)realloc(last_cpu_use, (info.cpu_count + 1) * sizeof(int)); last_cpu_cnt = info.cpu_count; if (last_cpu_use == nullptr) return 0; } info.cpu_usage = (float *)malloc((info.cpu_count + 1) * sizeof(float)); pthread_mutex_lock(&kstat_mtx); for (cpu = 1; cpu <= info.cpu_count; cpu++) { char stat_name[PATH_MAX]; unsigned long cpu_user, cpu_nice, cpu_system, cpu_idle; unsigned long cpu_use; cpu_stat_t *cs; kstat_t *ksp; snprintf(stat_name, PATH_MAX, "cpu_stat%d", cpu - 1); ksp = kstat_lookup(kstat, (char *)"cpu_stat", cpu - 1, stat_name); if (ksp == nullptr) continue; if (kstat_read(kstat, ksp, nullptr) == -1) continue; cs = (cpu_stat_t *)ksp->ks_data; cpu_idle = cs->cpu_sysinfo.cpu[CPU_IDLE]; cpu_user = cs->cpu_sysinfo.cpu[CPU_USER]; cpu_nice = cs->cpu_sysinfo.cpu[CPU_WAIT]; cpu_system = cs->cpu_sysinfo.cpu[CPU_KERNEL]; cpu_use = cpu_user + cpu_nice + cpu_system; info.cpu_usage[cpu] = (double)(cpu_use - last_cpu_use[cpu]) / d / 100.0; total_cpu_usage += info.cpu_usage[cpu]; last_cpu_use[cpu] = cpu_use; } pthread_mutex_unlock(&kstat_mtx); info.cpu_usage[0] = total_cpu_usage / info.cpu_count; return 0; } void free_cpu(struct text_object *) { /* no-op */ } void update_proc_entry(struct process *p) { psinfo_t proc; int fd; char pfn[PATH_MAX]; snprintf(pfn, PATH_MAX, "/proc/%d/psinfo", p->pid); /* Ignore errors here as the process can be gone */ if ((fd = open(pfn, O_RDONLY)) < 0) return; if (pread(fd, &proc, sizeof(psinfo_t), 0) != sizeof(psinfo_t)) { (void)close(fd); return; } (void)close(fd); free_and_zero(p->name); free_and_zero(p->basename); p->name = strndup(proc.pr_fname, text_buffer_size.get(*::state)); p->basename = strndup(proc.pr_fname, text_buffer_size.get(*::state)); p->uid = proc.pr_uid; /* see proc(4) */ p->amount = (double)proc.pr_pctcpu / (double)0x8000 * 100.0; p->rss = proc.pr_rssize * 1024; /* to bytes */ p->vsize = proc.pr_size * 1024; /* to bytes */ p->total_cpu_time = proc.pr_time.tv_sec * 100; /* to hundredths of secs */ if (proc.pr_lwp.pr_sname == 'O' || proc.pr_lwp.pr_sname == 'R') info.run_procs++; p->time_stamp = g_time; } void get_top_info(void) { DIR *dir; struct dirent *entry; if (!(dir = opendir("/proc"))) { return; } info.run_procs = 0; while ((entry = readdir(dir))) { pid_t pid; if (entry == nullptr) break; if (sscanf(entry->d_name, "%u", &pid) != 1) continue; update_proc_entry(get_process(pid)); } (void)closedir(dir); } /* * Because Solaris systems often have 100s or 1000s of disks, we don't collect * data for all of them but only for those mentioned in conkyrc. * Instead of disk's special file in SVR4 format, we use the driver name and * and the instance number to specify the disk or partition. For example: sd0, * ssd3, or sd5,b. */ int update_diskio(void) { unsigned int tot_read = 0; unsigned int tot_written = 0; update_kstat(); pthread_mutex_lock(&kstat_mtx); for (struct diskio_stat *cur = &stats; cur; cur = cur->next) { unsigned int read, written; kstat_io_t *ksio; kstat_t *ksp; if (cur->dev == nullptr) continue; if ((ksp = kstat_lookup(kstat, nullptr, -1, cur->dev)) == NULL) continue; if (kstat_read(kstat, ksp, nullptr) == -1) continue; ksio = (kstat_io_t *)ksp->ks_data; tot_read += read = (unsigned int)(ksio->nread / 512); tot_written += written = (unsigned int)(ksio->nwritten / 512); update_diskio_values(cur, read, written); } update_diskio_values(&stats, tot_read, tot_written); pthread_mutex_unlock(&kstat_mtx); return 0; } void get_battery_short_status(char *buffer, unsigned int n, const char *bat) { /* Not implemented */ (void)bat; if (buffer && n > 0) memset(buffer, 0, n); } void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size) { /* Not implemented */ if (p_client_buffer && client_buffer_size > 0) memset(p_client_buffer, 0, client_buffer_size); } void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size, const char *adapter) { /* Not implemented */ if (p_client_buffer && client_buffer_size > 0) memset(p_client_buffer, 0, client_buffer_size); } int get_battery_perct(const char *bat) { /* Not implemented */ (void)bat; return 1; } int get_entropy_poolsize(unsigned int *val) { /* Not implemented */ (void)val; return 1; } char get_freq(char *p_client_buffer, size_t client_buffer_size, const char *p_format, int divisor, unsigned int cpu) { char stat_name[PATH_MAX]; kstat_named_t *knp; snprintf(stat_name, PATH_MAX, "cpu_info%d", cpu - 1); knp = get_kstat("cpu_info", cpu - 1, stat_name, "current_clock_Hz"); if (knp == nullptr) return 0; snprintf(p_client_buffer, client_buffer_size, p_format, (float)knp->value.ui32 / divisor / 1000000.0); return 1; } int update_uptime(void) { kstat_named_t *knp; knp = get_kstat("unix", -1, "system_misc", "boot_time"); if (knp == nullptr) return 0; info.uptime = time(nullptr) - knp->value.ui32; return 1; } int open_acpi_temperature(const char *name) { /* Not implemented */ (void)name; return 1; } int get_entropy_avail(unsigned int *val) { /* Not implemented */ (void)val; return 1; } int update_load_average(void) { double load[3]; getloadavg(load, 3); info.loadavg[0] = (float)load[0]; info.loadavg[1] = (float)load[1]; info.loadavg[2] = (float)load[2]; return 0; } void get_cpu_count(void) { kstat_named_t *knp = get_kstat("unix", -1, "system_misc", "ncpus"); if (knp != nullptr) info.cpu_count = knp->value.ui32; }