/* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the Lesser GNU General Public License, version 3 * or later ("LGPL"), as published by the Free Software Foundation. * * 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. * * You should have received a copy of the Lesser 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 #include "logger.h" #include "shared_func.h" #include "fc_memory.h" #include "system_info.h" #ifdef OS_LINUX #include #include #include #else #ifdef OS_FREEBSD #include #include #if HAVE_USER_H == 1 #include #endif #if HAVE_VMMETER_H == 1 #include #endif #endif #endif int get_sys_total_mem_size(int64_t *mem_size) { #ifdef OS_LINUX struct sysinfo si; if (sysinfo(&si) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysinfo fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } *mem_size = si.totalram; return 0; #elif defined(OS_FREEBSD) int mib[2]; size_t len; mib[0] = CTL_HW; //mib[1] = HW_MEMSIZE; mib[1] = HW_PHYSMEM; len = sizeof(*mem_size); if (sysctl(mib, 2, mem_size, &len, NULL, 0) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } return 0; #else *mem_size = 0; logError("file: "__FILE__", line: %d, " "please port me!", __LINE__); return EOPNOTSUPP; #endif } int get_sys_cpu_count() { #if defined(OS_LINUX) || defined(OS_FREEBSD) return sysconf(_SC_NPROCESSORS_ONLN); #else logError("file: "__FILE__", line: %d, " "please port me!", __LINE__); return 0; #endif } #define TIMEVAL_TO_SECONDS(tv) \ ((double)tv.tv_sec + (double)tv.tv_usec / 1000000.00) #define SECONDS_TO_TIMEVAL(secs, tv) \ do { \ (tv).tv_sec = (time_t)secs; \ (tv).tv_usec = (secs - (tv).tv_sec) * 1000000; \ } while (0) int get_boot_time(struct timeval *boot_time) { #ifdef OS_LINUX char buff[256]; int64_t bytes; struct sysinfo si; bytes = sizeof(buff); if (getFileContentEx("/proc/uptime", buff, 0, &bytes) == 0) { double uptime; double btime; struct timeval current_time; if (sscanf(buff, "%lf", &uptime) == 1) { gettimeofday(¤t_time, NULL); btime = TIMEVAL_TO_SECONDS(current_time) - uptime; SECONDS_TO_TIMEVAL(btime, *boot_time); boot_time->tv_usec = 0; return 0; } } if (sysinfo(&si) != 0) { logError("file: "__FILE__", line: %d, " "call sysinfo fail, " "errno: %d, error info: %s", __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } boot_time->tv_sec = time(NULL) - si.uptime; boot_time->tv_usec = 0; return 0; #elif defined(OS_FREEBSD) size_t size; int mib[2]; mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof(struct timeval); if (sysctl(mib, 2, boot_time, &size, NULL, 0) == 0) { return 0; } else { boot_time->tv_sec = 0; boot_time->tv_usec = 0; logError("file: "__FILE__", line: %d, " "call sysctl fail, " "errno: %d, error info: %s", __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } #else boot_time->tv_sec = 0; boot_time->tv_usec = 0; logError("file: "__FILE__", line: %d, " "please port me!", __LINE__); return EOPNOTSUPP; #endif } #define SET_STATFS_FIELDS(left, right) \ do { \ left.f_type = right.f_type; \ left.f_bsize = right.f_bsize; \ left.f_blocks = right.f_blocks; \ left.f_bfree = right.f_bfree; \ left.f_bavail = right.f_bavail; \ left.f_files = right.f_files; \ left.f_ffree = right.f_ffree; \ } while (0) #define SET_MNT_FIELDS(left, fstypename, mntfromname, mntonname) \ do { \ snprintf(left.f_fstypename, sizeof(left.f_fstypename), "%s", fstypename); \ snprintf(left.f_mntfromname, sizeof(left.f_mntfromname), "%s", mntfromname); \ snprintf(left.f_mntonname, sizeof(left.f_mntonname), "%s", mntonname); \ } while (0) int get_mounted_filesystems(struct fast_statfs *stats, const int size, int *count) { #ifdef OS_LINUX const char *filename = "/proc/mounts"; FILE *fp; char *p; char *mntfromname; char *mntonname; char *fstypename; struct statfs buf; char line[1024]; int result; int i; *count = 0; fp = fopen(filename, "r"); if (fp == NULL) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "call fopen %s fail, " "errno: %d, error info: %s", __LINE__, filename, errno, STRERROR(errno)); return result; } memset(stats, 0, sizeof(struct fast_statfs) * size); result = 0; while (fgets(line, sizeof(line), fp) != NULL) { if (*count >= size) { result = ENOSPC; break; } p = line; mntfromname = strsep(&p, " \t"); mntonname = strsep(&p, " \t"); fstypename = strsep(&p, " \t"); snprintf(stats[*count].f_mntfromname, sizeof(stats[*count].f_mntfromname), "%s", mntfromname); snprintf(stats[*count].f_mntonname, sizeof(stats[*count].f_mntonname), "%s", mntonname); snprintf(stats[*count].f_fstypename, sizeof(stats[*count].f_fstypename), "%s", fstypename); (*count)++; } fclose(fp); for (i=0; i<*count; i++) { if (statfs(stats[i].f_mntonname, &buf) == 0) { SET_STATFS_FIELDS(stats[i], buf); #ifdef HAVE_FILE_SYSTEM_ID stats[i].f_fsid = buf.f_fsid; #endif } else { logWarning("file: "__FILE__", line: %d, " "call statfs fail, " "errno: %d, error info: %s", __LINE__, errno, STRERROR(errno)); } } return result; #elif defined(OS_FREEBSD) struct statfs *mnts; int result; int i; mnts = NULL; *count = getmntinfo(&mnts, 0); if (*count == 0) { result = errno != 0 ? errno : EPERM; logError("file: "__FILE__", line: %d, " "call getmntinfo fail, " "errno: %d, error info: %s", __LINE__, errno, STRERROR(errno)); return result; } if (*count <= size) { result = 0; } else { *count = size; result = ENOSPC; } for (i=0; i<*count; i++) { SET_STATFS_FIELDS(stats[i], mnts[i]); #ifdef HAVE_FILE_SYSTEM_ID stats[i].f_fsid = mnts[i].f_fsid; #endif SET_MNT_FIELDS(stats[i], mnts[i].f_fstypename, mnts[i].f_mntfromname, mnts[i].f_mntonname); } return result; #else *count = 0; logError("file: "__FILE__", line: %d, " "please port me!", __LINE__); return EOPNOTSUPP; #endif } #if defined(OS_LINUX) || defined(OS_FREEBSD) typedef struct fast_process_array { struct fast_process_info *procs; int alloc_size; int count; } FastProcessArray; #if defined(OS_LINUX) static int check_process_capacity(FastProcessArray *proc_array) { struct fast_process_info *procs; int alloc_size; int bytes; if (proc_array->alloc_size > proc_array->count) { return 0; } alloc_size = proc_array->alloc_size > 0 ? proc_array->alloc_size * 2 : 128; bytes = sizeof(struct fast_process_info) * alloc_size; procs = (struct fast_process_info *)fc_malloc(bytes); if (procs == NULL) { return ENOMEM; } memset(procs, 0, bytes); if (proc_array->count > 0) { memcpy(procs, proc_array->procs, sizeof(struct fast_process_info) * proc_array->count); free(proc_array->procs); } proc_array->alloc_size = alloc_size; proc_array->procs = procs; return 0; } static void parse_proc_stat(char *buff, const int len, struct fast_process_info *process, unsigned long long *starttime) { char *p; char *end; char *start; int cmd_len; if (len == 0) { process->field_count = 0; return; } end = buff + len; process->pid = strtol(buff, &p, 10); p++; //skip space start = p; while (p < end) { if (*p == ' ' || *p == '\t') { if (*start == '(') { if (*(p - 1) == ')') { break; } } else { break; } } p++; } if (p == end) { process->field_count = 1; return; } if (*start == '(') { start++; cmd_len = p - start - 1; } else { cmd_len = p - start; } if (cmd_len >= sizeof(process->comm)) { cmd_len = sizeof(process->comm) - 1; } memcpy(process->comm, start, cmd_len); p++; //skip space process->field_count = 2 + sscanf(p, "%c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld " "%ld %ld %llu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " "%lu %lu %d %d %u %u %llu %lu %ld", &process->state, &process->ppid, &process->pgrp, &process->session, &process->tty_nr, &process->tpgid, &process->flags, &process->minflt, &process->cminflt, &process->majflt, &process->cmajflt, &process->utime, &process->stime, &process->cutime, &process->cstime, &process->priority, &process->nice, &process->num_threads, &process->itrealvalue, starttime, &process->vsize, &process->rss, &process->rsslim, &process->startcode, &process->endcode, &process->startstack, &process->kstkesp, &process->kstkeip, &process->signal, &process->blocked, &process->sigignore, &process->sigcatch, &process->wchan, &process->nswap, &process->cnswap, &process->exit_signal, &process->processor, &process->rt_priority, &process->policy, &process->delayacct_blkio_ticks, &process->guest_time, &process->cguest_time); } int get_processes(struct fast_process_info **processes, int *count) { const char *dirname = "/proc"; char filename[128]; char buff[4096]; DIR *dir; struct timeval boot_time; struct dirent *ent; FastProcessArray proc_array; int64_t bytes; unsigned long long starttime; int tickets; int result; int len; int i; tickets = sysconf(_SC_CLK_TCK); if (tickets == 0) { tickets = 100; } dir = opendir(dirname); if (dir == NULL) { *count = 0; *processes = NULL; logError("file: "__FILE__", line: %d, " "call opendir %s fail, " "errno: %d, error info: %s", __LINE__, dirname, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } result = 0; proc_array.procs = NULL; proc_array.alloc_size = 0; proc_array.count = 0; while ((ent=readdir(dir)) != NULL) { len = strlen(ent->d_name); for (i=0; id_name[i] >= '0' && ent->d_name[i] <= '9')) { break; } } if (i < len) //not digital string { continue; } if ((result=check_process_capacity(&proc_array)) != 0) { break; } sprintf(filename, "%s/%s/stat", dirname, ent->d_name); bytes = sizeof(buff); if (getFileContentEx(filename, buff, 0, &bytes) != 0) { continue; } get_boot_time(&boot_time); parse_proc_stat(buff, bytes, proc_array.procs + proc_array.count, &starttime); SECONDS_TO_TIMEVAL(TIMEVAL_TO_SECONDS(boot_time) + (double)starttime / (double)tickets, proc_array.procs[proc_array.count].starttime); proc_array.procs[proc_array.count].starttime.tv_usec = 0; proc_array.count++; } closedir(dir); *count = proc_array.count; *processes = proc_array.procs; return result; } int get_sysinfo(struct fast_sysinfo *info) { struct sysinfo si; get_boot_time(&info->boot_time); if (sysinfo(&si) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysinfo fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } info->loads[0] = si.loads[0] / (double)(1 << SI_LOAD_SHIFT); info->loads[1] = si.loads[1] / (double)(1 << SI_LOAD_SHIFT), info->loads[2] = si.loads[2] / (double)(1 << SI_LOAD_SHIFT); info->totalram = si.totalram; info->freeram = si.freeram; info->sharedram = si.sharedram; info->bufferram = si.bufferram; info->totalswap = si.totalswap; info->freeswap = si.freeswap; info->procs = si.procs; return 0; } #elif defined(OS_FREEBSD) #ifdef DARWIN #define ki_pid kp_proc.p_pid #define ki_comm kp_proc.p_comm #define ki_ppid kp_eproc.e_ppid #define ki_start kp_proc.p_starttime #define ki_flag kp_proc.p_flag #define ki_stat kp_proc.p_stat #define ki_sigignore kp_proc.p_sigignore #define ki_sigcatch kp_proc.p_sigcatch #define ki_priority kp_proc.p_priority #define ki_ruid kp_eproc.e_pcred.p_ruid #define ki_rgid kp_eproc.e_pcred.p_rgid #define GET_SIGNAL(sig) sig #else #define ki_priority ki_pri.pri_level #define GET_SIGNAL(sig) *((int *)&sig) #endif int get_processes(struct fast_process_info **processes, int *count) { struct kinfo_proc *procs; struct fast_process_info *process; int mib[4]; size_t size; int bytes; int nproc; int i; bool success; *count = 0; *processes = NULL; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_ALL; mib[3] = 0; size = 0; if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } nproc = size / sizeof(struct kinfo_proc); if (nproc == 0) { return ENOENT; } success = false; procs = NULL; for (i=0; i<10; i++) { nproc += 32; if (procs != NULL) { free(procs); } size = sizeof(struct kinfo_proc) * nproc; procs = (struct kinfo_proc *)fc_malloc(size); if (procs == NULL) { return ENOMEM; } if (sysctl(mib, 4, procs, &size, NULL, 0) == 0) { success = true; break; } if (errno != ENOMEM) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); free(procs); return errno != 0 ? errno : EPERM; } } if (!success) { free(procs); return ENOSPC; } nproc = size / sizeof(struct kinfo_proc); bytes = sizeof(struct fast_process_info) * nproc; *processes = (struct fast_process_info *)fc_malloc(bytes); if (*processes == NULL) { free(procs); return ENOMEM; } memset(*processes, 0, bytes); process = *processes; for (i=0; ifield_count = 9; snprintf(process->comm, sizeof(process->comm), "%s", procs[i].ki_comm); process->pid = procs[i].ki_pid; process->ppid = procs[i].ki_ppid; process->starttime = procs[i].ki_start; process->flags = procs[i].ki_flag; process->state = procs[i].ki_stat; process->sigignore = GET_SIGNAL(procs[i].ki_sigignore); process->sigcatch = GET_SIGNAL(procs[i].ki_sigcatch); process->priority = procs[i].ki_priority; //process->uid = procs[i].ki_ruid; //process->gid = procs[i].ki_rgid; process++; } free(procs); *count = nproc; return 0; } int get_sysinfo(struct fast_sysinfo *info) { int mib[4]; size_t size; struct loadavg loads; #if HAVE_VMMETER_H == 1 struct vmtotal vm; #endif #ifdef VM_SWAPUSAGE struct xsw_usage sw_usage; #endif memset(info, 0, sizeof(struct fast_sysinfo)); get_boot_time(&info->boot_time); mib[0] = CTL_VM; mib[1] = VM_LOADAVG; size = sizeof(loads); if (sysctl(mib, 2, &loads, &size, NULL, 0) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); } else if (loads.fscale > 0) { info->loads[0] = (double)loads.ldavg[0] / loads.fscale; info->loads[1] = (double)loads.ldavg[1] / loads.fscale; info->loads[2] = (double)loads.ldavg[2] / loads.fscale; } mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_ALL; mib[3] = 0; size = 0; if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); } else { info->procs = size / sizeof(struct kinfo_proc); } get_sys_total_mem_size((int64_t *)&info->totalram); #if HAVE_VMMETER_H == 1 mib[0] = CTL_VM; mib[1] = VM_METER; size = sizeof(vm); if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); } else { int page_size; page_size = sysconf(_SC_PAGESIZE); info->freeram = vm.t_free * page_size; info->sharedram = vm.t_rmshr * page_size; } #endif #ifdef VM_SWAPUSAGE mib[0] = CTL_VM; mib[1] = VM_SWAPUSAGE; size = sizeof(sw_usage); if (sysctl(mib, 2, &sw_usage, &size, NULL, 0) != 0) { logError("file: "__FILE__", line: %d, " \ "call sysctl fail, " \ "errno: %d, error info: %s", \ __LINE__, errno, STRERROR(errno)); } else { info->totalswap = sw_usage.xsu_total; info->freeswap = sw_usage.xsu_avail; } #endif return 0; } #endif #endif int get_kernel_version(Version *version) { struct utsname name; char *p; int numbers[2]; int i; if (uname(&name) < 0) { logError("file: "__FILE__", line: %d, " "call uname fail, errno: %d, error info: %s", __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EFAULT; } numbers[0] = numbers[1] = 0; p = name.release; for (i=0; i<2; i++) { p = strchr(p, '.'); if (p == NULL) { break; } p++; numbers[i] = strtol(p, NULL, 10); } version->major = strtol(name.release, NULL, 10); version->minor = numbers[0]; version->patch = numbers[1]; return 0; } #ifdef OS_LINUX int get_device_block_size(const char *device, int *block_size) { int result; int fd; size_t bs; if ((fd=open(device, O_RDONLY | O_CLOEXEC)) < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "open device %s fail, errno: %d, error info: %s", __LINE__, device, result, strerror(result)); return result; } if (ioctl(fd, BLKSSZGET, &bs) == 0) { *block_size = bs; result = 0; } else { result = errno != 0 ? errno : EPERM; logError("file: "__FILE__", line: %d, " "ioctl device %s fail, errno: %d, error info: %s", __LINE__, device, result, strerror(result)); } close(fd); return result; } static int get_block_size_by_write(const char *path, int *block_size) { #define MAX_BLK_SIZE (128 * 1024) char tmp_filename[PATH_MAX]; char *buff; int result; int fd; if ((result=posix_memalign((void **)&buff, MAX_BLK_SIZE, MAX_BLK_SIZE)) != 0) { logError("file: "__FILE__", line: %d, " "posix_memalign %d bytes fail, " "errno: %d, error info: %s", __LINE__, MAX_BLK_SIZE, result, STRERROR(result)); return result; } snprintf(tmp_filename, sizeof(tmp_filename), "%s/.blksize-test.tmp", path); if ((fd=open(tmp_filename, O_WRONLY | O_CREAT | O_DIRECT | O_CLOEXEC, 0755)) < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "open file %s fail, errno: %d, error info: %s", __LINE__, tmp_filename, result, strerror(result)); free(buff); return result; } result = EINVAL; *block_size = 512; while (*block_size <= MAX_BLK_SIZE) { if (write(fd, buff, *block_size) == *block_size) { result = 0; break; } result = errno != 0 ? errno : EINTR; if (result == EINTR) { continue; } if (result != EINVAL) { logError("file: "__FILE__", line: %d, " "write to file %s fail, errno: %d, error info: %s", __LINE__, tmp_filename, result, strerror(result)); break; } *block_size *= 2; } free(buff); close(fd); unlink(tmp_filename); return result; } int get_path_block_size(const char *path, int *block_size) { char dev_path[64]; struct stat statbuf; int result; if (stat(path, &statbuf) != 0) { result = errno != 0 ? errno : EPERM; logError("file: "__FILE__", line: %d, " "stat %s fail, errno: %d, error info: %s", __LINE__, path, result, strerror(result)); return result; } if (S_ISBLK(statbuf.st_mode)) { return get_device_block_size(path, block_size); } if (!S_ISDIR(statbuf.st_mode)) { logError("file: "__FILE__", line: %d, " "%s is NOT a directory!", __LINE__, path); return ENOTDIR; } sprintf(dev_path, "/dev/block/%d:%d", (int)major(statbuf.st_dev), (int)minor(statbuf.st_dev)); if (access(dev_path, R_OK) == 0) { if ((result=get_device_block_size(dev_path, block_size)) == 0) { return 0; } } return get_block_size_by_write(path, block_size); } int get_groups(const pid_t pid, const gid_t gid, const int size, gid_t *list) { #define GROUPS_TAG_STR "\nGroups:" #define GROUPS_TAG_LEN (sizeof(GROUPS_TAG_STR) - 1) char filename[64]; char buff[1024]; char *p; char *end; int fd; int len; gid_t val; int count; sprintf(filename, "/proc/%d/status", pid); fd = open(filename, O_RDONLY); if (fd < 0) { return 0; } len = read(fd, buff, sizeof(buff)); close(fd); if (len <= 0) { return 0; } buff[len - 1] = '\0'; if ((p=strstr(buff, GROUPS_TAG_STR)) == NULL) { return 0; } p += GROUPS_TAG_LEN; count = 0; while (count < size) { val = strtoul(p, &end, 10); if (end == p) { break; } if (val != gid) { list[count++] = val; } p = end; } return count; } #elif defined(OS_FREEBSD) int get_groups(const pid_t pid, const gid_t gid, const int size, gid_t *list) { int mibpath[4]; struct kinfo_proc kp; size_t kplen; int count; int i; #if defined(CTL_KERN) && defined(KERN_PROC) && defined(KERN_PROC_PID) mibpath[0] = CTL_KERN; mibpath[1] = KERN_PROC; mibpath[2] = KERN_PROC_PID; #else kplen = 4; sysctlnametomib("kern.proc.pid", mibpath, &kplen); #endif mibpath[3] = pid; kplen = sizeof(kp); memset(&kp,0,sizeof(kp)); if (sysctl(mibpath, 4, &kp, &kplen, NULL, 0) != 0) { return 0; } #if defined(DARWIN) #define ki_ngroups kp_eproc.e_ucred.cr_ngroups #define ki_groups kp_eproc.e_ucred.cr_groups #endif count = 0; for (i=0; i