#define _KERNEL 1 /* for struct file */ #include #undef _KERNEL #include "sigar.h" #include "sigar_private.h" #include "sigar_util.h" #include "sigar_os.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for proc_port */ #include #include #include #include /* for odm api */ #include #include #include #include "user_v5.h" #include "utmp_v5.h" #include /* not defined in aix 4.3 */ #ifndef SBITS #define SBITS 16 #endif #ifndef PTHRDSINFO_RUSAGE_START #define PTHRDSINFO_RUSAGE_START 0x00000001 #define PTHRDSINFO_RUSAGE_STOP 0x00000002 #define PTHRDSINFO_RUSAGE_COLLECT 0x00000004 #endif /* * from libperfstat.h: * "To calculate the load average, divide the numbers by (1<." */ #define FIXED_TO_DOUBLE(x) (((double)x) / (1<koffsets[i] = klist[i].n_value; } return SIGAR_OK; } static int kread(sigar_t *sigar, void *data, int size, long offset) { if (sigar->kmem < 0) { return SIGAR_EPERM_KMEM; } if (lseek(sigar->kmem, offset, SEEK_SET) != offset) { return errno; } if (read(sigar->kmem, data, size) != size) { return errno; } return SIGAR_OK; } int sigar_os_open(sigar_t **sigar) { int status, i; void *dlhandle; int kmem = -1; vminfo_func_t vminfo = NULL; struct utsname name; if ((dlhandle = dlopen("/unix", RTLD_NOW))) { vminfo = (vminfo_func_t)dlsym(dlhandle, "vmgetinfo"); dlclose(dlhandle); } kmem = open("/dev/kmem", O_RDONLY); *sigar = malloc(sizeof(**sigar)); (*sigar)->getvminfo = vminfo; (*sigar)->getprocfd = NULL; /*XXX*/ (*sigar)->kmem = kmem; (*sigar)->dmem = -1; (*sigar)->pagesize = 0; (*sigar)->boot_time = 0; (*sigar)->last_pid = -1; (*sigar)->pinfo = NULL; (*sigar)->cpuinfo = NULL; (*sigar)->cpuinfo_size = 0; SIGAR_ZERO(&(*sigar)->swaps); SIGAR_ZERO(&(*sigar)->perfstat); i = getpagesize(); while ((i >>= 1) > 0) { (*sigar)->pagesize++; } if (kmem > 0) { if ((status = get_koffsets(*sigar)) != SIGAR_OK) { free(*sigar); return status; } } (*sigar)->cpu_mhz = -1; (*sigar)->model[0] = '\0'; (*sigar)->self_path[0] = '\0'; uname(&name); (*sigar)->aix_version = atoi(name.version); (*sigar)->thrusage = PTHRDSINFO_RUSAGE_STOP; (*sigar)->diskmap = NULL; return SIGAR_OK; } static void swaps_free(swaps_t *swaps); int sigar_os_close(sigar_t *sigar) { swaps_free(&sigar->swaps); if (sigar->kmem > 0) { close(sigar->kmem); } if (sigar->dmem > 0) { close(sigar->dmem); } if (sigar->pinfo) { free(sigar->pinfo); } if (sigar->cpuinfo) { free(sigar->cpuinfo); } if (sigar->perfstat.handle) { dlclose(sigar->perfstat.handle); } if (sigar->diskmap) { sigar_cache_destroy(sigar->diskmap); } if (sigar->thrusage == PTHRDSINFO_RUSAGE_START) { struct rusage usage; sigar->perfstat.thread_rusage(&usage, PTHRDSINFO_RUSAGE_STOP); } free(sigar); return SIGAR_OK; } char *sigar_os_error_string(sigar_t *sigar, int err) { switch (err) { case SIGAR_EPERM_KMEM: return "Failed to open /dev/kmem for reading"; default: return NULL; } } static int proc_module_get_self(void *data, char *name, int len) { sigar_t *sigar = (sigar_t *)data; char *ptr = rindex(name, '/'); if (!ptr) { return SIGAR_OK; } if (strnEQ(ptr+1, "libsigar-", 9)) { *ptr = '\0'; /* chop libsigar-powerpc-ibm-aix-4.3.x.so */ SIGAR_SSTRCPY(sigar->self_path, name); if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "detected sigar-lib='%s'", sigar->self_path); } return !SIGAR_OK; /* break loop */ } return SIGAR_OK; } static char *sigar_get_self_path(sigar_t *sigar) { if (sigar->self_path[0] == '\0') { sigar_proc_modules_t procmods; procmods.module_getter = proc_module_get_self; procmods.data = sigar; sigar_proc_modules_get(sigar, sigar_pid_get(sigar), &procmods); if (sigar->self_path[0] == '\0') { /* dont try again */ SIGAR_SSTRCPY(sigar->self_path, "."); } } return sigar->self_path; } /* * the perfstat api is only supported in aix 5.2+ * in order to be binary compatible with 4.3 and 5.1 * we must jump through some hoops. libperfstat.a * is a static library, we need dynamic. * libsigar_aixperfstat.so is juat a proxy to libperfstat.a */ #define SIGAR_AIXPERFSTAT "/libsigar_aixperfstat.so" static int sigar_perfstat_init(sigar_t *sigar) { void *handle; char libperfstat[SIGAR_PATH_MAX], *path; int len; if (sigar->perfstat.avail == 1) { return SIGAR_OK; } if (sigar->perfstat.avail == -1) { return ENOENT; } path = sigar_get_self_path(sigar); len = strlen(path); memcpy(&path[0], sigar->self_path, len); memcpy(&path[len], SIGAR_AIXPERFSTAT, sizeof(SIGAR_AIXPERFSTAT)); if (!(handle = dlopen(path, RTLD_LOCAL|RTLD_LAZY))) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "failed to open '%s': %s", path, sigar_strerror(sigar, errno)); } sigar->perfstat.avail = -1; return errno; } sigar->perfstat.thread_rusage = (thread_rusage_func_t)dlsym(handle, "sigar_thread_rusage"); if (!sigar->perfstat.thread_rusage) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "dlsym(sigar_thread_rusage) failed: %s", dlerror()); } } sigar->perfstat.cpu_total = (perfstat_cpu_total_func_t)dlsym(handle, "sigar_perfstat_cpu_total"); if (!sigar->perfstat.cpu_total) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "dlsym(sigar_perfstat_cpu_total) failed: %s", dlerror()); } dlclose(handle); sigar->perfstat.avail = -1; return ENOENT; } sigar->perfstat.cpu = (perfstat_cpu_func_t)dlsym(handle, "sigar_perfstat_cpu"); if (!sigar->perfstat.cpu) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "dlsym(sigar_perfstat_cpu) failed: %s", dlerror()); } dlclose(handle); sigar->perfstat.avail = -1; return ENOENT; } sigar->perfstat.swap = (perfstat_swap_func_t)dlsym(handle, "sigar_perfstat_pagingspace"); if (!sigar->perfstat.swap) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "dlsym(sigar_perfstat_pagingspace) failed: %s", dlerror()); } dlclose(handle); sigar->perfstat.avail = -1; return ENOENT; } sigar->perfstat.disk = (perfstat_disk_func_t)dlsym(handle, "sigar_perfstat_disk"); if (!sigar->perfstat.disk) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "dlsym(sigar_perfstat_disk) failed: %s", dlerror()); } dlclose(handle); sigar->perfstat.avail = -1; return ENOENT; } sigar->perfstat.avail = 1; sigar->perfstat.handle = handle; return SIGAR_OK; } #define PAGESHIFT(v) \ ((v) << sigar->pagesize) int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) { struct vminfo vm; #if 0 /* XXX: wtf, this is supposed to be a modern way * to get the same values below. yet it works on 4.3.3 * but not 5.1 */ if (!sigar->getvminfo) { return EPERM; } if (sigar->getvminfo(&vm, VMINFO, sizeof(vm)) != 0) { return errno; } #else int status; status = kread(sigar, &vm, sizeof(vm), sigar->koffsets[KOFFSET_VMINFO]); if (status != SIGAR_OK) { return status; } #endif mem->total = PAGESHIFT(vm.memsizepgs); /* lsattr -El sys0 -a realmem */ mem->free = PAGESHIFT(vm.numfrb); mem->used = mem->total - mem->free; mem->actual_used = mem->used; mem->actual_free = mem->free; mem->shared = SIGAR_FIELD_NOTIMPL; sigar_mem_calc_ram(sigar, mem); return SIGAR_OK; } static void swaps_free(swaps_t *swaps) { if (swaps->num) { int i; for (i=0; inum; i++) { free(swaps->devs[i]); } free(swaps->devs); swaps->num = 0; } } /* * there is no public api for parsing this file. * well, there is something, but its super ugly and requires * linking 2 static libraries (libodm and something else) * maybe will switch to that if it can add value elsewhere too. */ #define SWAPSPACES "/etc/swapspaces" static int swaps_get(swaps_t *swaps) { FILE *fp; char buf[512]; char *ptr; struct stat statbuf; if (stat(SWAPSPACES, &statbuf) < 0) { return errno; } /* only re-parse if file has changed */ if (swaps->mtime == statbuf.st_mtime) { return 0; } swaps->mtime = statbuf.st_mtime; /* easier to just start from scratch */ swaps_free(swaps); if (!(fp = fopen(SWAPSPACES, "r"))) { return errno; } while ((ptr = fgets(buf, sizeof(buf), fp))) { if (!isalpha(*ptr)) { continue; } if (strchr(ptr, ':')) { int len; ptr = fgets(buf, sizeof(buf), fp); while (isspace(*ptr)) { ++ptr; } if (strncmp(ptr, "dev", 3)) { continue; } ptr += 3; while (isspace(*ptr) || (*ptr == '=')) { ++ptr; } len = strlen(ptr); ptr[len-1] = '\0'; /* -1 == chomp \n */ swaps->devs = realloc(swaps->devs, swaps->num+1 * sizeof(char *)); swaps->devs[swaps->num] = malloc(len); memcpy(swaps->devs[swaps->num], ptr, len); swaps->num++; } } fclose(fp); return 0; } /* * documented in aix tech ref, * but this prototype is not in any friggin header file. * struct pginfo is in sys/vminfo.h */ int swapqry(char *path, struct pginfo *info); static int sigar_swap_get_swapqry(sigar_t *sigar, sigar_swap_t *swap) { int status, i; if ((status = swaps_get(&sigar->swaps)) != SIGAR_OK) { return status; } if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[swap] pagesize=%d, shift=%d", getpagesize(), sigar->pagesize); } swap->total = swap->free = 0; for (i=0; iswaps.num; i++) { struct pginfo info; status = swapqry(sigar->swaps.devs[i], &info); if (status != 0) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[swap] swapqry(%s) failed: %s", sigar->swaps.devs[i], sigar_strerror(sigar, errno)); } continue; } if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[swap] %s total=%d/%d, free=%d/%d", sigar->swaps.devs[i], info.size, PAGESHIFT(info.size), info.free, PAGESHIFT(info.free)); } swap->total += PAGESHIFT(info.size); /* lsps -a */ swap->free += PAGESHIFT(info.free); } swap->used = swap->total - swap->free; return SIGAR_OK; } #define SWAP_DEV(ps) \ ((ps.type == LV_PAGING) ? \ ps.id.lv_paging.vgname : \ ps.id.nfs_paging.filename) #define SWAP_MB_TO_BYTES(v) ((v) * (1024 * 1024)) static int sigar_swap_get_perfstat(sigar_t *sigar, sigar_swap_t *swap) { perfstat_pagingspace_t ps; perfstat_id_t id; id.name[0] = '\0'; SIGAR_ZERO(swap); do { if (sigar->perfstat.swap(&id, &ps, sizeof(ps), 1) != 1) { if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[swap] dev=%s query failed: %s", SWAP_DEV(ps), sigar_strerror(sigar, errno)); } continue; } if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[swap] dev=%s: active=%s, " "total=%lluMb, used=%lluMb", SWAP_DEV(ps), ((ps.active == 1) ? "yes" : "no"), ps.mb_size, ps.mb_used); } if (ps.active != 1) { continue; } /* convert MB sizes to bytes */ swap->total += SWAP_MB_TO_BYTES(ps.mb_size); swap->used += SWAP_MB_TO_BYTES(ps.mb_used); } while (id.name[0] != '\0'); swap->free = swap->total - swap->used; return SIGAR_OK; } int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) { if (sigar_perfstat_init(sigar) == SIGAR_OK) { sigar_log(sigar, SIGAR_LOG_DEBUG, "[swap] using libperfstat"); return sigar_swap_get_perfstat(sigar, swap); } else { sigar_log(sigar, SIGAR_LOG_DEBUG, "[swap] using /dev/kmem"); return sigar_swap_get_swapqry(sigar, swap); } } int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) { int i, status; struct sysinfo data; perfstat_cpu_total_t cpu_data; if (sigar_perfstat_init(sigar) == SIGAR_OK) { sigar_log(sigar, SIGAR_LOG_DEBUG, "[cpu] using libperfstat"); if (sigar->perfstat.cpu_total(&cpu_data, sizeof(cpu_data)) == 1) { cpu->user = cpu_data.user; cpu->nice = SIGAR_FIELD_NOTIMPL; /* N/A */ cpu->sys = cpu_data.sys; cpu->idle = cpu_data.idle; cpu->wait = cpu_data.wait; cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait; return SIGAR_OK; } } sigar_log(sigar, SIGAR_LOG_DEBUG, "[cpu] using /dev/kmem"); status = kread(sigar, &data, sizeof(data), sigar->koffsets[KOFFSET_SYSINFO]); if (status != SIGAR_OK) { return status; } cpu->user = data.cpu[CPU_USER]; cpu->nice = SIGAR_FIELD_NOTIMPL; /* N/A */ cpu->sys = data.cpu[CPU_KERNEL]; cpu->idle = data.cpu[CPU_IDLE]; cpu->total = 0; for (i=0; itotal += data.cpu[i]; } return SIGAR_OK; } /* * other possible metrics we could add: * struct cpuinfo { * long cpu[CPU_NTIMES]; * long pswitch; * long syscall; * long sysread; * long syswrite; * long sysfork; * long sysexec; * long readch; * long writech; * long iget; * long namei; * long dirblk; * long msg; * long sema; * long bread; * long bwrite; * long lread; * long lwrite; * long phread; * long phwrite; * }; */ static int sigar_cpu_list_get_kmem(sigar_t *sigar, sigar_cpu_list_t *cpulist) { int status, i, j; int ncpu = _system_configuration.ncpus; /* this can change */ int size = ncpu * sizeof(struct cpuinfo); if (sigar->cpuinfo_size < size) { sigar->cpuinfo = realloc(sigar->cpuinfo, size); } status = kread(sigar, sigar->cpuinfo, size, sigar->koffsets[KOFFSET_CPUINFO]); if (status != SIGAR_OK) { return status; } sigar_cpu_list_create(cpulist); for (i=0; idata[cpulist->number++]; info = &sigar->cpuinfo[i]; cpu->user = info->cpu[CPU_USER]; cpu->nice = 0; /* N/A */ cpu->sys = info->cpu[CPU_KERNEL]; cpu->idle = info->cpu[CPU_IDLE]; cpu->total = 0; for (j=0; jtotal += info->cpu[j]; } } return SIGAR_OK; } static int sigar_cpu_list_get_pstat(sigar_t *sigar, sigar_cpu_list_t *cpulist) { perfstat_cpu_t data; int i, ncpu = _system_configuration.ncpus; /* this can change */ perfstat_id_t id; id.name[0] = '\0'; sigar_cpu_list_create(cpulist); for (i=0; idata[cpulist->number++]; if (SIGAR_LOG_IS_DEBUG(sigar)) { sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "cpu%d perfstat_id='%s'", i, id.name); } if (sigar->perfstat.cpu(&id, &data, sizeof(data), 1) == 1) { cpu->user = data.user; cpu->nice = SIGAR_FIELD_NOTIMPL; /* N/A */ cpu->sys = data.sys; cpu->idle = data.idle; cpu->wait = data.wait; cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait; } else { sigar_log_printf(sigar, SIGAR_LOG_ERROR, "cpu%d perfstat_cpu(%s) failed: %s", i, id.name, sigar_strerror(sigar, errno)); SIGAR_ZERO(cpu); } } return SIGAR_OK; } int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) { if (sigar_perfstat_init(sigar) == SIGAR_OK) { sigar_log(sigar, SIGAR_LOG_DEBUG, "[cpu_list] using libperfstat"); return sigar_cpu_list_get_pstat(sigar, cpulist); } else { sigar_log(sigar, SIGAR_LOG_DEBUG, "[cpu_list] using /dev/kmem"); return sigar_cpu_list_get_kmem(sigar, cpulist); } } static int boot_time_v4(int fd, time_t *time) { struct utmp data; do { if (read(fd, &data, sizeof(data)) != sizeof(data)) { return errno; } } while (data.ut_type != BOOT_TIME); *time = data.ut_time; return SIGAR_OK; } static int boot_time_v5(int fd, time_t *time) { struct utmp_v5 data; do { if (read(fd, &data, sizeof(data)) != sizeof(data)) { return errno; } } while (data.ut_type != BOOT_TIME); if (data.ut_time != 0) { *time = data.ut_time; } else { /* XXX: dunno wtf is going on here. * this exact code above works as expected * in a standalone program. * sizeof(utmp_v5) is the same if compiled on 4.3 * or 5.2, this workaround hack will have todo for now. */ *time = data.__time_t_space; } return SIGAR_OK; } static int boot_time(sigar_t *sigar, time_t *time) { int utmp, status; if ((utmp = open(UTMP_FILE, O_RDONLY)) < 0) { return errno; } if (sigar->aix_version == 4) { status = boot_time_v4(utmp, time); } else { status = boot_time_v5(utmp, time); } close(utmp); return status; } int sigar_uptime_get(sigar_t *sigar, sigar_uptime_t *uptime) { if (sigar->boot_time == 0) { int status; time_t time; if ((status = boot_time(sigar, &time)) != SIGAR_OK) { return status; } sigar->boot_time = time; } uptime->uptime = time(NULL) - sigar->boot_time; return SIGAR_OK; } int sigar_loadavg_get(sigar_t *sigar, sigar_loadavg_t *loadavg) { int status, i; int data[3]; perfstat_cpu_total_t cpu_data; if (sigar_perfstat_init(sigar) == SIGAR_OK) { sigar_log(sigar, SIGAR_LOG_DEBUG, "[loadavg] using libperfstat"); if (sigar->perfstat.cpu_total(&cpu_data, sizeof(cpu_data)) == 1) { for (i=0; i<3; i++) { loadavg->loadavg[i] = FIXED_TO_DOUBLE(cpu_data.loadavg[i]); } return SIGAR_OK; } else { sigar_log_printf(sigar, SIGAR_LOG_ERROR, "perfstat_cpu_total failed: %s", sigar_strerror(sigar, errno)); } } sigar_log(sigar, SIGAR_LOG_DEBUG, "[loadavg] using /dev/kmem"); status = kread(sigar, &data, sizeof(data), sigar->koffsets[KOFFSET_LOADAVG]); if (status != SIGAR_OK) { return status; } for (i=0; i<3; i++) { loadavg->loadavg[i] = FIXED_TO_DOUBLE(data[i]); } return SIGAR_OK; } int sigar_proc_list_get(sigar_t *sigar, sigar_proc_list_t *proclist) { pid_t pid = 0; struct procsinfo info; sigar_proc_list_create(proclist); for (;;) { int num = getprocs(&info, sizeof(info), NULL, 0, &pid, 1); if (num == 0) { break; } SIGAR_PROC_LIST_GROW(proclist); proclist->data[proclist->number++] = info.pi_pid; } return SIGAR_OK; } int sigar_proc_stat_get(sigar_t *sigar, sigar_proc_stat_t *procstat) { int status = /* XXX optimize */ sigar_proc_count(sigar, &procstat->total); return status; } static int sigar_getprocs(sigar_t *sigar, sigar_pid_t pid) { int status, num; time_t timenow = time(NULL); if (sigar->pinfo == NULL) { sigar->pinfo = malloc(sizeof(*sigar->pinfo)); } if (sigar->last_pid == pid) { if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { return SIGAR_OK; } } sigar->last_pid = pid; sigar->last_getprocs = timenow; num = getprocs(sigar->pinfo, sizeof(*sigar->pinfo), NULL, 0, &pid, 1); if (num != 1) { return ESRCH; } return SIGAR_OK; } int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_mem_t *procmem) { int status = sigar_getprocs(sigar, pid); struct procsinfo *pinfo = sigar->pinfo; if (status != SIGAR_OK) { return status; } procmem->size = PAGESHIFT(pinfo->pi_size); procmem->vsize = PAGESHIFT(pinfo->pi_dvm); procmem->share = PAGESHIFT(pinfo->pi_sdsize); procmem->rss = PAGESHIFT(pinfo->pi_drss + pinfo->pi_trss); procmem->resident = SIGAR_FIELD_NOTIMPL; /* N/A */ return SIGAR_OK; } int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_cred_t *proccred) { int status = sigar_getprocs(sigar, pid); struct procsinfo *pinfo = sigar->pinfo; if (status != SIGAR_OK) { return status; } proccred->uid = pinfo->pi_cred.cr_ruid; proccred->euid = pinfo->pi_cred.cr_uid; if (proccred->uid == -1) { /* * aix 5.2 has a process named 'jfsz' * where uid is '-1', getpwuid returns EPERM */ proccred->uid = proccred->euid = 0; } proccred->gid = pinfo->pi_cred.cr_rgid; proccred->egid = pinfo->pi_cred.cr_gid; return SIGAR_OK; } int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_time_t *proctime) { int status = sigar_getprocs(sigar, pid); struct procsinfo *pinfo = sigar->pinfo; if (status != SIGAR_OK) { return status; } proctime->start_time = pinfo->pi_start; proctime->start_time *= 1000; /* convert to ms */ proctime->user = pinfo->pi_utime; proctime->sys = pinfo->pi_stime; proctime->total = proctime->user + proctime->sys; return SIGAR_OK; } int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_state_t *procstate) { int status = sigar_getprocs(sigar, pid); struct procsinfo *pinfo = sigar->pinfo; if (status != SIGAR_OK) { return status; } SIGAR_SSTRCPY(procstate->name, pinfo->pi_comm); procstate->ppid = pinfo->pi_ppid; procstate->nice = pinfo->pi_nice; procstate->tty = pinfo->pi_ttyd; procstate->priority = SIGAR_FIELD_NOTIMPL; /* XXX getthrds() */ switch (pinfo->pi_state) { case SACTIVE: procstate->state = 'R'; break; case SIDL: procstate->state = 'D'; break; case SSTOP: procstate->state = 'S'; break; case SZOMB: procstate->state = 'Z'; break; case SSWAP: procstate->state = 'S'; break; } return SIGAR_OK; } int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_args_t *procargs) { /* XXX if buffer is not large enough args are truncated */ char buffer[8192], *ptr; struct procsinfo pinfo; pinfo.pi_pid = pid; if (getargs(&pinfo, sizeof(pinfo), buffer, sizeof(buffer)) != 0) { return errno; } sigar_proc_args_create(procargs); ptr = buffer; while (*ptr) { int alen = strlen(ptr)+1; char *arg = malloc(alen); SIGAR_PROC_ARGS_GROW(procargs); memcpy(arg, ptr, alen); procargs->data[procargs->number++] = arg; ptr += alen; } return SIGAR_OK; } int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_env_t *procenv) { /* XXX if buffer is not large enough args are truncated */ char buffer[8192], *ptr; struct procsinfo pinfo; pinfo.pi_pid = pid; if (getevars(&pinfo, sizeof(pinfo), buffer, sizeof(buffer)) != 0) { return errno; } ptr = buffer; while (*ptr) { char *val = strchr(ptr, '='); int klen, vlen, status; char key[128]; /* XXX is there a max key size? */ if (val == NULL) { /* not key=val format */ procenv->env_getter(procenv->data, ptr, strlen(ptr), NULL, 0); break; } klen = val - ptr; SIGAR_SSTRCPY(key, ptr); key[klen] = '\0'; ++val; vlen = strlen(val); status = procenv->env_getter(procenv->data, key, klen, val, vlen); if (status != SIGAR_OK) { /* not an error; just stop iterating */ break; } ptr += (klen + 1 + vlen + 1); } return SIGAR_OK; } /* * V[45]_sigar_proc_fd_get routines are exactly * the same except for sizeof(uinfo). */ static int V5_sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_fd_t *procfd) { int i; struct procsinfo pinfo; struct user_v5 uinfo; /* V5 */ procfd->total = 0; pinfo.pi_pid = pid; if (getuser(&pinfo, sizeof(pinfo), &uinfo, sizeof(uinfo)) != 0) { if (errno == EINVAL) { return SIGAR_ENOTIMPL; } return errno; } /* see sys/user.h */ for (i=0; itotal++; } } return SIGAR_OK; } static int V4_sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_fd_t *procfd) { int i; struct procsinfo pinfo; struct user uinfo; /* V4 */ procfd->total = 0; pinfo.pi_pid = pid; if (getuser(&pinfo, sizeof(pinfo), &uinfo, sizeof(uinfo)) != 0) { if (errno == EINVAL) { return errno; } } /* see sys/user.h */ for (i=0; itotal++; } } return SIGAR_OK; } int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_fd_t *procfd) { if (sigar->getprocfd == NULL) { /* * XXX should determine aix version in sigar_os_open * and set function pointer there. for now try v4 * first, if that fails try v5. only costs 1 extra * call to getuser on v5 for the lifetime of the * sigar. */ int status = V4_sigar_proc_fd_get(sigar, pid, procfd); if (status == SIGAR_OK) { sigar->getprocfd = V4_sigar_proc_fd_get; return SIGAR_OK; } sigar->getprocfd = V5_sigar_proc_fd_get; } return sigar->getprocfd(sigar, pid, procfd); } int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_exe_t *procexe) { return SIGAR_ENOTIMPL; } static int sigar_proc_modules_local_get(sigar_t *sigar, sigar_proc_modules_t *procmods) { struct ld_info *info; char *buffer; int size = 2048, status; unsigned int offset; buffer = malloc(size); while ((loadquery(L_GETINFO, buffer, size) == -1) && (errno == ENOMEM)) { size += 2048; buffer = realloc(buffer, size); } info = (struct ld_info *)buffer; do { char *name = info->ldinfo_filename; status = procmods->module_getter(procmods->data, name, strlen(name)); if (status != SIGAR_OK) { /* not an error; just stop iterating */ free(buffer); return status; } offset = info->ldinfo_next; info = (struct ld_info *)((char*)info + offset); } while(offset); free(buffer); return SIGAR_OK; } int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_modules_t *procmods) { if (pid == sigar_pid_get(sigar)) { return sigar_proc_modules_local_get(sigar, procmods); } else { return SIGAR_ENOTIMPL; } } #define SIGAR_MICROSEC2NANO(s) \ ((sigar_uint64_t)(s) * (sigar_uint64_t)1000) #define TIME_NSEC(t) \ (SIGAR_SEC2NANO((t).tv_sec) + SIGAR_MICROSEC2NANO((t).tv_usec)) int sigar_thread_cpu_get(sigar_t *sigar, sigar_uint64_t id, sigar_thread_cpu_t *cpu) { struct rusage usage; int retval; sigar_perfstat_init(sigar); if (!sigar->perfstat.thread_rusage) { return SIGAR_ENOTIMPL; } if (sigar->thrusage != PTHRDSINFO_RUSAGE_START) { sigar->thrusage = PTHRDSINFO_RUSAGE_START; retval = sigar->perfstat.thread_rusage(&usage, PTHRDSINFO_RUSAGE_START); if (retval != 0) { return retval; } } retval = sigar->perfstat.thread_rusage(&usage, PTHRDSINFO_RUSAGE_COLLECT); if (retval != 0) { return retval; } cpu->user = TIME_NSEC(usage.ru_utime); cpu->sys = TIME_NSEC(usage.ru_stime); cpu->total = TIME_NSEC(usage.ru_utime) + TIME_NSEC(usage.ru_stime); return SIGAR_OK; } int sigar_os_fs_type_get(sigar_file_system_t *fsp) { return fsp->type; } /* another one documented in aix tech ref * with no friggin prototype in any header file. */ int mntctl(int command, int size, char *buffer); int sigar_file_system_list_get(sigar_t *sigar, sigar_file_system_list_t *fslist) { int i, size, num; char *buf, *mntlist; /* get required size */ if (mntctl(MCTL_QUERY, sizeof(size), (char *)&size) < 0) { return errno; } mntlist = buf = malloc(size); if ((num = mntctl(MCTL_QUERY, size, buf)) < 0) { free(buf); return errno; } sigar_file_system_list_create(fslist); for (i=0; ivmt_length; SIGAR_FILE_SYSTEM_LIST_GROW(fslist); fsp = &fslist->data[fslist->number++]; switch (ent->vmt_gfstype) { case MNT_AIX: typename = "aix"; fsp->type = SIGAR_FSTYPE_LOCAL_DISK; break; case MNT_JFS: typename = "jfs"; fsp->type = SIGAR_FSTYPE_LOCAL_DISK; break; case MNT_NFS: case MNT_NFS3: typename = "nfs"; fsp->type = SIGAR_FSTYPE_NETWORK; break; case MNT_CDROM: fsp->type = SIGAR_FSTYPE_CDROM; break; case MNT_SFS: case MNT_CACHEFS: case MNT_AUTOFS: default: if (ent->vmt_flags & MNT_REMOTE) { fsp->type = SIGAR_FSTYPE_NETWORK; } else { fsp->type = SIGAR_FSTYPE_NONE; } } SIGAR_SSTRCPY(fsp->dir_name, vmt2dataptr(ent, VMT_STUB)); devname = vmt2dataptr(ent, VMT_OBJECT); if (fsp->type == SIGAR_FSTYPE_NETWORK) { char *hostname = vmt2dataptr(ent, VMT_HOSTNAME); #if 0 /* XXX: these do not seem reliable */ int hostname_len = vmt2datasize(ent, VMT_HOSTNAME)-1; /* -1 == skip '\0' */ int devname_len = vmt2datasize(ent, VMT_OBJECT); /* includes '\0' */ #else int hostname_len = strlen(hostname); int devname_len = strlen(devname) + 1; #endif int total_len = hostname_len + devname_len + 1; /* 1 == strlen(":") */ if (total_len > sizeof(fsp->dev_name)) { /* justincase - prevent overflow. chances: slim..none */ SIGAR_SSTRCPY(fsp->dev_name, devname); } else { /* sprintf(fsp->devname, "%s:%s", hostname, devname) */ char *ptr = fsp->dev_name; memcpy(ptr, hostname, hostname_len); ptr += hostname_len; *ptr++ = ':'; memcpy(ptr, devname, devname_len); } } else { SIGAR_SSTRCPY(fsp->dev_name, devname); } /* we set fsp->type, just looking up sigar.c:fstype_names[type] */ sigar_fs_type_get(fsp); if (typename == NULL) { typename = fsp->type_name; } SIGAR_SSTRCPY(fsp->sys_type_name, typename); } free(buf); return SIGAR_OK; } /* XXX this is exactly the same as linux and hpux, solaris is darn close */ #define SIGAR_FS_BLOCKS_TO_BYTES(buf, f) \ ((buf.f * (buf.f_bsize / 512)) >> 1) #define LSPV_CMD "/usr/sbin/lspv" #define FSDEV_ID(sb) (sb.st_ino + sb.st_dev) typedef struct { char *name; long addr; } aix_diskio_t; static void diskio_free(void *data) { aix_diskio_t *diskio = (aix_diskio_t *)data; free(diskio->name); free(diskio); } /* * dont have per-partition metrics on aix. * need to build a mount point => diskname map. * see 'lspv -l hdisk0' for example. */ static int create_diskmap_v4(sigar_t *sigar) { FILE *fp = popen(LSPV_CMD, "r"); char buffer[BUFSIZ], *ptr; sigar->diskmap = sigar_cache_new(25); sigar->diskmap->free_value = diskio_free; if (!fp) { return ENOENT; } while ((ptr = fgets(buffer, sizeof(buffer), fp))) { FILE *lfp; char cmd[256], disk[56]; char *s; if (strstr(ptr, " None")) { continue; /* no volume group */ } if ((s = strchr(ptr, ' '))) { *s = '\0'; } strcpy(disk, ptr); sprintf(cmd, LSPV_CMD " -l %s", disk); if (!(lfp = popen(cmd, "r"))) { continue; } (void)fgets(buffer, sizeof(buffer), lfp); /* skip disk: */ (void)fgets(buffer, sizeof(buffer), lfp); /* skip headers */ while ((ptr = fgets(buffer, sizeof(buffer), lfp))) { sigar_cache_entry_t *ent; struct stat sb; int retval; /* LV NAME LPs PPs DISTRIBUTION */ ptr = sigar_skip_multiple_token(ptr, 4); SIGAR_SKIP_SPACE(ptr); if ((s = strchr(ptr, '\n'))) { *s = '\0'; } if (strEQ(ptr, "N/A")) { continue; } retval = stat(ptr, &sb); if (retval == 0) { aix_diskio_t *diskio = malloc(sizeof(*diskio)); diskio->name = strdup(disk); diskio->addr = -1; ent = sigar_cache_get(sigar->diskmap, FSDEV_ID(sb)); ent->value = diskio; } } pclose(lfp); } pclose(fp); } static int create_diskmap_v5(sigar_t *sigar) { int i, total, num; perfstat_disk_t *disk; perfstat_id_t id; sigar_perfstat_init(sigar); if (!sigar->perfstat.disk) { return SIGAR_ENOTIMPL; } total = sigar->perfstat.disk(NULL, NULL, sizeof(*disk), 0); if (total < 1) { return ENOENT; } disk = malloc(total * sizeof(*disk)); id.name[0] = '\0'; num = sigar->perfstat.disk(&id, disk, sizeof(*disk), total); if (num < 1) { free(disk); return ENOENT; } sigar->diskmap = sigar_cache_new(25); sigar->diskmap->free_value = diskio_free; odm_initialize(); for (i=0; iname, "label", 0, &num))) { retval = stat(attr->value, &sb); if (retval == 0) { aix_diskio_t *diskio = malloc(sizeof(*diskio)); diskio->name = strdup(disk[i].name); diskio->addr = -1; ent = sigar_cache_get(sigar->diskmap, FSDEV_ID(sb)); ent->value = diskio; } free(attr); } } odm_free_list(dv, &info); } free(disk); odm_terminate(); return SIGAR_OK; } static int create_diskmap(sigar_t *sigar) { if (create_diskmap_v5(sigar) != SIGAR_OK) { return create_diskmap_v4(sigar); } return SIGAR_OK; } static int get_perfstat_disk_metrics(sigar_t *sigar, sigar_file_system_usage_t *fsusage, aix_diskio_t *diskio) { perfstat_disk_t disk; perfstat_id_t id; sigar_perfstat_init(sigar); if (!sigar->perfstat.disk) { return SIGAR_ENOTIMPL; } SIGAR_SSTRCPY(id.name, diskio->name); if (sigar->perfstat.disk(&id, &disk, sizeof(disk), 1) != 1) { return ENOENT; } fsusage->disk_reads = disk.rblks; fsusage->disk_writes = disk.wblks; return SIGAR_OK; } static int get_disk_metrics(sigar_t *sigar, sigar_file_system_usage_t *fsusage, aix_diskio_t *diskio) { int i, cnt, fd, status; struct iostat iostat; struct dkstat dkstat, *dp; struct nlist nl[] = { { "iostat" }, }; status = get_perfstat_disk_metrics(sigar, fsusage, diskio); if (status == SIGAR_OK) { return SIGAR_OK; } if (sigar->dmem == -1) { if ((sigar->dmem = open("/dev/mem", O_RDONLY)) <= 0) { return errno; } } fd = sigar->dmem; if (diskio->addr != -1) { int status; lseek(fd, diskio->addr, SEEK_SET); read(fd, &dkstat, sizeof(dkstat)); if (strEQ(diskio->name, dkstat.diskname)) { fsusage->disk_reads = dkstat.dk_rblks; fsusage->disk_writes = dkstat.dk_wblks; status = SIGAR_OK; } else { status = ENOENT; } return status; } i = knlist(nl, 1, sizeof(struct nlist)); if (i == -1) { return errno; } if (nl[i].n_value == 0) { return ENOENT; } lseek(fd, nl[0].n_value, SEEK_SET); read(fd, &iostat, sizeof(iostat)); for (dp = iostat.dkstatp, cnt = iostat.dk_cnt; cnt && dp; --cnt, dp = dkstat.dknextp) { lseek(fd, (long)dp, SEEK_SET); read(fd, &dkstat, sizeof(dkstat)); if (strEQ(diskio->name, dkstat.diskname)) { fsusage->disk_reads = dkstat.dk_rblks; fsusage->disk_writes = dkstat.dk_wblks; diskio->addr = (long)dp; break; } } return SIGAR_OK; } int sigar_file_system_usage_get(sigar_t *sigar, const char *dirname, sigar_file_system_usage_t *fsusage) { sigar_cache_entry_t *ent; struct stat sb; int status; struct statfs buf; if (statfs((char *)dirname, &buf) != 0) { return errno; } fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_blocks); fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bfree); fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bavail); fsusage->files = buf.f_files; fsusage->free_files = buf.f_ffree; fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); SIGAR_DISK_STATS_NOTIMPL(fsusage); if (!sigar->diskmap) { status = create_diskmap(sigar); if (status != SIGAR_OK) { return SIGAR_OK; } } status = stat(dirname, &sb); if (status == 0) { sigar_cache_entry_t *ent = sigar_cache_get(sigar->diskmap, FSDEV_ID(sb)); if (!ent->value) { return SIGAR_OK; } get_disk_metrics(sigar, fsusage, (aix_diskio_t *)ent->value); } return SIGAR_OK; } /* from sys/systemcfg.h, not defined in 4.3 headers */ #ifndef POWER_4 #define POWER_4 0x0800 #endif #ifndef POWER_MPC7450 #define POWER_MPC7450 0x1000 #endif #ifndef POWER_5 #define POWER_5 0x2000 #endif static char *sigar_get_odm_model(sigar_t *sigar) { if (sigar->model[0] == '\0') { struct CuAt *odm_obj; int num; odm_initialize(); if ((odm_obj = getattr("proc0", "type", 0, &num))) { SIGAR_SSTRCPY(sigar->model, odm_obj->value); free(odm_obj); } odm_terminate(); } return sigar->model; } #define SIGAR_CPU_CACHE_SIZE \ (_system_configuration.L2_cache_size / 1024) static int sigar_get_cpu_mhz_perfstat(sigar_t *sigar) { perfstat_cpu_total_t data; if (sigar_perfstat_init(sigar) == SIGAR_OK) { if (sigar->perfstat.cpu_total(&data, sizeof(data)) == 1) { sigar->cpu_mhz = data.processorHZ / 1000000; return SIGAR_OK; } else { sigar_log_printf(sigar, SIGAR_LOG_ERROR, "perfstat_cpu_total failed: %s", sigar_strerror(sigar, errno)); } } return ENOENT; } static int sigar_get_cpu_mhz(sigar_t *sigar) { if (sigar->cpu_mhz == SIGAR_FIELD_NOTIMPL) { if (sigar_get_cpu_mhz_perfstat(sigar) != SIGAR_OK) { sigar_uint64_t cache_size = SIGAR_CPU_CACHE_SIZE; switch (cache_size) { case 1024: sigar->cpu_mhz = 333; break; case 4096: sigar->cpu_mhz = 400; break; case 8192: sigar->cpu_mhz = 450; break; default: sigar->cpu_mhz = SIGAR_FIELD_NOTIMPL; break; } } } return sigar->cpu_mhz; } int sigar_cpu_info_list_get(sigar_t *sigar, sigar_cpu_info_list_t *cpu_infos) { int i; int ncpu = _system_configuration.ncpus; /* this can change */ /*XXX should only do this once*/ sigar_cpu_info_list_create(cpu_infos); for (i=0; idata[cpu_infos->number++]; info->cache_size = SIGAR_CPU_CACHE_SIZE; info->mhz = sigar_get_cpu_mhz(sigar); switch (_system_configuration.architecture) { case POWER_RS: arch = "Power Classic"; break; case POWER_PC: arch = "PowerPC"; break; case IA64: arch = "IA64"; break; default: arch = "PowerPC"; /* what else could it be */ break; } if (*arch == 'P') { SIGAR_SSTRCPY(info->vendor, "IBM"); switch (_system_configuration.implementation) { case POWER_RS1: model = "RS1"; break; case POWER_RSC: model = "RSC"; break; case POWER_RS2: model = "RS2"; break; case POWER_601: model = "601"; break; case POWER_603: model = "603"; break; case POWER_604: model = "604"; break; case POWER_620: model = "620"; break; case POWER_630: model = "630"; break; case POWER_A35: model = "A35"; break; case POWER_RS64II: model = "RS64-II"; break; case POWER_RS64III: model = "RS64-III"; break; case POWER_4: model = "POWER4"; break; case POWER_MPC7450: model = "MPC7450"; break; case POWER_5: model = "POWER5"; break; default: break; } } else if (*arch == 'I') { SIGAR_SSTRCPY(info->vendor, "Intel"); switch (_system_configuration.implementation) { case IA64_M1: model = "M1"; break; case IA64_M2: model = "M2"; break; default: break; } } else { SIGAR_SSTRCPY(info->vendor, "IBM"); break; } if (model) { snprintf(info->model, sizeof(info->model), "%s %s", arch, model); } else { SIGAR_SSTRCPY(info->model, arch); } } return SIGAR_OK; } int sigar_net_route_list_get(sigar_t *sigar, sigar_net_route_list_t *routelist) { sigar_net_route_t *route; sigar_net_route_list_create(routelist); return SIGAR_OK; } int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, sigar_net_interface_stat_t *ifstat) { int status; struct ifnet data; caddr_t offset = 0; char if_name[32]; status = kread(sigar, &offset, sizeof(offset), sigar->koffsets[KOFFSET_IFNET]); if (status != SIGAR_OK) { return status; } for (; offset; offset = (caddr_t)data.if_next) { status = kread(sigar, &data, sizeof(data), (long)offset); if (status != SIGAR_OK) { return status; } status = kread(sigar, if_name, sizeof(if_name), (long)&data.if_name[0]); if (status != SIGAR_OK) { return status; } /* XXX if_name is 'en' or 'lo', not 'en0' or 'lo0' */ if (!strnEQ(if_name, name, strlen(if_name))) { continue; } ifstat->rx_bytes = data.if_ibytes; ifstat->rx_packets = data.if_ipackets; ifstat->rx_errors = data.if_ierrors; ifstat->rx_dropped = data.if_iqdrops; ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL; ifstat->rx_frame = SIGAR_FIELD_NOTIMPL; ifstat->tx_bytes = data.if_obytes; ifstat->tx_packets = data.if_opackets; ifstat->tx_errors = data.if_oerrors; ifstat->tx_dropped = SIGAR_FIELD_NOTIMPL; ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL; ifstat->tx_collisions = data.if_collisions; ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL; return SIGAR_OK; } return ENXIO; } int sigar_net_connection_list_get(sigar_t *sigar, sigar_net_connection_list_t *connlist, int flags) { return SIGAR_ENOTIMPL; } /* derived from pidentd's k_aix432.c */ int sigar_proc_port_get(sigar_t *sigar, unsigned long port, sigar_pid_t *pidp) { struct procsinfo pinfo; struct fdsinfo finfo; pid_t pid = 0; for (;;) { int fd, status; int num = getprocs(&pinfo, sizeof(pinfo), &finfo, sizeof(finfo), &pid, 1); if (num == 0) { break; } if ((pinfo.pi_state == 0) || (pinfo.pi_state == SZOMB)) { continue; } for (fd = 0; fd < pinfo.pi_maxofile; fd++) { struct file file; struct socket socket, *sockp; struct protosw protosw; struct domain domain; struct inpcb inpcb; long ptr; if (!(ptr = (long)finfo.pi_ufd[fd].fp)) { continue; } status = kread(sigar, &file, sizeof(file), ptr); if (status != SIGAR_OK) { continue; } if (file.f_type != DTYPE_SOCKET) { continue; } if (!(sockp = (struct socket *)file.f_data)) { continue; } status = kread(sigar, &socket, sizeof(socket), (long)sockp); if (status != SIGAR_OK) { continue; } if (!(ptr = (long)socket.so_proto)) { continue; } status = kread(sigar, &protosw, sizeof(protosw), ptr); if (status != SIGAR_OK) { continue; } if (protosw.pr_protocol != IPPROTO_TCP) { continue; } if (!(ptr = (long)protosw.pr_domain)) { continue; } status = kread(sigar, &domain, sizeof(domain), ptr); if (status != SIGAR_OK) { continue; } if ((domain.dom_family != AF_INET) && domain.dom_family != AF_INET6) { continue; } if (!(ptr = (long)socket.so_pcb)) { continue; } status = kread(sigar, &inpcb, sizeof(inpcb), ptr); if (status != SIGAR_OK) { continue; } if (sockp != inpcb.inp_socket) { continue; } if (inpcb.inp_lport != port) { continue; } *pidp = pinfo.pi_pid; return SIGAR_OK; } } return ENOENT; }