#include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* define _FreeBSD_version where applicable */ #if __FreeBSD__ >= 2 #include #endif #include #include #include /* KERN_PROC_* */ #include /* struct passwd */ #include /* struct group */ #include /* struct kinfo_proc prereq*/ #if __FreeBSD__ >= 5 #define cv bsd_cv #endif #include /* struct kinfo_proc */ #if __FreeBSD__ >= 5 #undef cv #endif #include /* O_RDONLY */ #include /* _POSIX2_LINE_MAX */ #define PATH_DEV_NULL "/dev/null" #define TIME_FRAC(t) ((double)(t).tv_sec + (double)(t).tv_usec/1000000) #define P_FLAG(f) ((kp->ki_flag & f) ? 1 : 0) #define KI_FLAG(f) ((kp->ki_kiflag & f) ? 1 : 0) #if __FreeBSD_version < 500000 #define NO_FREEBSD_4x(a) (-1) #define NO_FREEBSD_4x_pv(a) ("") #else #define NO_FREEBSD_4x(a) (a) #define NO_FREEBSD_4x_pv(a) (a) #endif #if __FreeBSD_version < 600000 #define NO_FREEBSD_5x(a) (-1) #define NO_FREEBSD_5x_pv(a) ("") #else #define NO_FREEBSD_5x(a) (a) #define NO_FREEBSD_5x_pv(a) (a) #endif static int proc_info_mib[4] = { -1, -1, -1, -1 }; struct kinfo_proc *_proc_request (kvm_t *kd, int request, int param, int *pnr) { struct kinfo_proc *kip; switch(request) { case 2: kip = kvm_getprocs(kd, KERN_PROC_PGRP, param, pnr); break; case 3: kip = kvm_getprocs(kd, KERN_PROC_SESSION, param, pnr); break; case 5: kip = kvm_getprocs(kd, KERN_PROC_UID, param, pnr); break; case 6: kip = kvm_getprocs(kd, KERN_PROC_RUID, param, pnr); break; #if __FreeBSD_version >= 600000 case 10: kip = kvm_getprocs(kd, KERN_PROC_RGID, param, pnr); break; case 11: kip = kvm_getprocs(kd, KERN_PROC_GID, param, pnr); break; #endif case 0: default: kip = kvm_getprocs(kd, KERN_PROC_ALL, 0, pnr); break; } return(kip); } void store_uid (HV *h, const char *field, uid_t uid) { struct passwd *pw; size_t flen; size_t len; flen = strlen(field); if (!(pw = getpwuid(uid))) { /* shouldn't ever happen... */ hv_store(h, field, flen, newSViv(uid), 0); } else { len = strlen(pw->pw_name); hv_store(h, field, flen, newSVpvn(pw->pw_name,len), 0); } } void store_gid (HV *h, const char *field, gid_t gid) { struct group *gr; size_t flen; size_t len; flen = strlen(field); if (!(gr = getgrgid(gid))) { /* shouldn't ever happen... */ hv_store(h, field, flen, newSViv(gid), 0); } else { len = strlen(gr->gr_name); hv_store(h, field, flen, newSVpvn(gr->gr_name,len), 0); } } #if __FreeBSD_version < 500000 #define ACFLAG_FIELD kp_proc.p_acflag #define COMM_FIELD kp_proc.p_comm #define ESTCPU_FIELD kp_proc.p_estcpu #define FLAG_FIELD kp_eproc.e_jobc #define JOBC_FIELD kp_eproc.e_flag #define LASTCPU_FIELD kp_proc.p_lastcpu #define LOCK_FIELD kp_proc.p_lock #define LOGIN_FIELD kp_eproc.e_login #define NICE_FIELD kp_proc.p_nice #define ONCPU_FIELD kp_proc.p_oncpu #define PCTCPU_FIELD kp_proc.p_pctcpu #define PGID_FIELD kp_eproc.e_pgid #define PID_FIELD kp_proc.p_pid #define PPID_FIELD kp_eproc.e_ppid #define RQINDEX_FIELD kp_proc.p_rqindex #define RSSIZE_FIELD kp_eproc.e_xrssize #define RUNTIME_FIELD kp_proc.p_runtime #define SLPTIME_FIELD kp_proc.p_slptime #define SWRSS_FIELD kp_eproc.e_xswrss #define SWTIME_FIELD kp_proc.p_swtime #define TPGID_FIELD kp_eproc.e_tpgid #define TSIZE_FIELD kp_eproc.e_xsize #define WMESG_FIELD kp_eproc.e_wmesg #define XSTAT_FIELD kp_proc.p_xstat #else #define ACFLAG_FIELD ki_acflag #define COMM_FIELD ki_comm #define ESTCPU_FIELD ki_estcpu #define FLAG_FIELD ki_flag #define JOBC_FIELD ki_jobc #define LASTCPU_FIELD ki_lastcpu #define LOCK_FIELD ki_lock #define LOGIN_FIELD ki_login #define NICE_FIELD ki_nice #define ONCPU_FIELD ki_oncpu #define PCTCPU_FIELD ki_pctcpu #define PGID_FIELD ki_pgid #define PID_FIELD ki_pid #define PPID_FIELD ki_ppid #define RQINDEX_FIELD ki_rqindex #define RSSIZE_FIELD ki_rssize #define RUNTIME_FIELD ki_runtime #define SLPTIME_FIELD ki_slptime #define SWRSS_FIELD ki_swrss #define SWTIME_FIELD ki_swtime #define TPGID_FIELD ki_tpgid #define TSIZE_FIELD ki_tsize #define WMESG_FIELD ki_wmesg #define XSTAT_FIELD ki_xstat #endif HV *_procinfo (struct kinfo_proc *kp, int resolve) { HV *h; const char *nlistf, *memf; kvm_t *kd; char errbuf[_POSIX2_LINE_MAX]; char **argv; SV *argsv; size_t len; struct group *gr; short g; AV *grlist; #if __FreeBSD_version >= 500000 struct rusage *rp; #endif h = (HV *)sv_2mortal((SV *)newHV()); hv_store(h, "pid", 3, newSViv(kp->PID_FIELD), 0); hv_store(h, "ppid", 4, newSViv(kp->PPID_FIELD), 0); hv_store(h, "pgid", 4, newSViv(kp->PGID_FIELD), 0); hv_store(h, "tpgid", 5, newSViv(kp->TPGID_FIELD), 0); hv_store(h, "jobc", 4, newSViv(kp->JOBC_FIELD), 0); hv_store(h, "tsize", 5, newSViv(kp->TSIZE_FIELD), 0); hv_store(h, "rssize", 6, newSViv(kp->RSSIZE_FIELD), 0); hv_store(h, "swrss", 5, newSViv(kp->SWRSS_FIELD), 0); hv_store(h, "acflag", 6, newSViv(kp->ACFLAG_FIELD), 0); hv_store(h, "flag", 4, newSViv(kp->FLAG_FIELD), 0); hv_store(h, "pctcpu", 6, newSViv(kp->PCTCPU_FIELD), 0); hv_store(h, "estcpu", 6, newSViv(kp->ESTCPU_FIELD), 0); hv_store(h, "xstat", 5, newSViv(kp->XSTAT_FIELD), 0); hv_store(h, "slptime", 7, newSViv(kp->SLPTIME_FIELD), 0); hv_store(h, "swtime", 6, newSViv(kp->SWTIME_FIELD), 0); hv_store(h, "runtime", 7, newSViv(kp->RUNTIME_FIELD), 0); hv_store(h, "lock", 4, newSViv(kp->LOCK_FIELD), 0); hv_store(h, "rqindex", 7, newSViv(kp->RQINDEX_FIELD), 0); hv_store(h, "oncpu", 5, newSViv(kp->ONCPU_FIELD), 0); hv_store(h, "lastcpu", 7, newSViv(kp->LASTCPU_FIELD), 0); hv_store(h, "nice", 4, newSViv(kp->NICE_FIELD), 0); hv_store(h, "wmesg", 5, newSVpv(kp->WMESG_FIELD, 0), 0); hv_store(h, "login", 5, newSVpv(kp->LOGIN_FIELD, 0), 0); hv_store(h, "comm", 4, newSVpv(kp->COMM_FIELD, 0), 0); hv_store(h, "sid", 3, newSViv(NO_FREEBSD_4x(kp->ki_sid)), 0); hv_store(h, "tsid", 4, newSViv(NO_FREEBSD_4x(kp->ki_tsid)), 0); if (!resolve) { /* numeric user and group ids */ hv_store(h, "uid", 3, newSViv(NO_FREEBSD_4x(kp->ki_uid)), 0); hv_store(h, "ruid", 4, newSViv(NO_FREEBSD_4x(kp->ki_ruid)), 0); hv_store(h, "svuid", 5, newSViv(NO_FREEBSD_4x(kp->ki_svuid)), 0); hv_store(h, "rgid", 4, newSViv(NO_FREEBSD_4x(kp->ki_rgid)), 0); hv_store(h, "svgid", 5, newSViv(NO_FREEBSD_4x(kp->ki_svgid)), 0); } else { NO_FREEBSD_4x(store_uid(h, "uid", kp->ki_uid)); NO_FREEBSD_4x(store_uid(h, "ruid", kp->ki_ruid)); NO_FREEBSD_4x(store_uid(h, "svuid", kp->ki_svuid)); NO_FREEBSD_4x(store_gid(h, "rgid", kp->ki_rgid)); NO_FREEBSD_4x(store_gid(h, "svgid", kp->ki_svgid)); } grlist = (AV *)sv_2mortal((SV *)newAV()); #if __FreeBSD_version < 500000 /* not available in FreeBSD 4.x */ hv_store(h, "args", 4, newSViv(-1), 0); #else /* attributes available only in FreeBSD 5.x, 6.x */ nlistf = memf = PATH_DEV_NULL; kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); if (!kd) { warn( "kvm_openfiles failed: %s\n", errbuf ); argv = 0; } else { argv = kvm_getargv(kd, kp, 0); } if( argv && *argv ) { #ifdef NEVER len = strlen(*argv); argsv = newSVpvn(*argv, len); while (*++argv) { sv_catpvn(argsv, " ", 1); sv_catpvn(argsv, *argv, strlen(*argv)); len += strlen(*argv)+1; } hv_store(h, "args", 4, argsv, 0); #else hv_store(h, "args", 4, newSVpvn("", 0), 0); #endif } else { hv_store(h, "args", 4, newSVpvn("", 0), 0); } if (kd) { kvm_close(kd); } /* deal with groups array */ for (g = 0; g < kp->ki_ngroups; ++g) { if (resolve && (gr = getgrgid(kp->ki_groups[g]))) { av_push(grlist, newSVpvn(gr->gr_name, strlen(gr->gr_name))); } else { av_push(grlist, newSViv(kp->ki_groups[g])); } } #endif hv_store(h, "groups", 6, newRV((SV *)grlist), 0); hv_store(h, "ngroups", 7, newSViv(NO_FREEBSD_4x(kp->ki_ngroups)), 0); hv_store(h, "size", 4, newSViv(NO_FREEBSD_4x(kp->ki_size)), 0); hv_store(h, "dsize", 5, newSViv(NO_FREEBSD_4x(kp->ki_dsize)), 0); hv_store(h, "ssize", 5, newSViv(NO_FREEBSD_4x(kp->ki_ssize)), 0); hv_store(h, "start", 5, newSVnv(NO_FREEBSD_4x(TIME_FRAC(kp->ki_start))), 0); hv_store(h, "childtime", 9, newSVnv(NO_FREEBSD_4x(TIME_FRAC(kp->ki_childtime))), 0); hv_store(h, "advlock", 7, newSViv(NO_FREEBSD_4x(P_FLAG(P_ADVLOCK))), 0); hv_store(h, "controlt", 8, newSViv(NO_FREEBSD_4x(P_FLAG(P_CONTROLT))), 0); hv_store(h, "kthread", 7, newSViv(NO_FREEBSD_4x(P_FLAG(P_KTHREAD))), 0); hv_store(h, "noload", 6, newSViv(NO_FREEBSD_4x(P_FLAG(P_NOLOAD))), 0); hv_store(h, "ppwait", 6, newSViv(NO_FREEBSD_4x(P_FLAG(P_PPWAIT))), 0); hv_store(h, "profil", 6, newSViv(NO_FREEBSD_4x(P_FLAG(P_PROFIL))), 0); hv_store(h, "stopprof", 8, newSViv(NO_FREEBSD_4x(P_FLAG(P_STOPPROF))), 0); hv_store(h, "sugid", 5, newSViv(NO_FREEBSD_4x(P_FLAG(P_SUGID))), 0); hv_store(h, "system", 6, newSViv(NO_FREEBSD_4x(P_FLAG(P_SYSTEM))), 0); hv_store(h, "single_exit", 11, newSViv(NO_FREEBSD_4x(P_FLAG(P_SINGLE_EXIT))), 0); hv_store(h, "traced", 6, newSViv(NO_FREEBSD_4x(P_FLAG(P_TRACED))), 0); hv_store(h, "waited", 6, newSViv(NO_FREEBSD_4x(P_FLAG(P_WAITED))), 0); hv_store(h, "wexit", 5, newSViv(NO_FREEBSD_4x(P_FLAG(P_WEXIT))), 0); hv_store(h, "exec", 4, newSViv(NO_FREEBSD_4x(P_FLAG(P_EXEC))), 0); hv_store(h, "hadthreads", 10, newSViv(NO_FREEBSD_5x(P_FLAG(P_HADTHREADS))), 0); hv_store(h, "kiflag", 6, newSViv(NO_FREEBSD_4x(kp->ki_kiflag)), 0); hv_store(h, "locked", 6, newSViv(NO_FREEBSD_4x(KI_FLAG(KI_LOCKBLOCK))), 0); hv_store(h, "isctty", 6, newSViv(NO_FREEBSD_4x(KI_FLAG(KI_CTTY))), 0); hv_store(h, "issleader", 9, newSViv(NO_FREEBSD_4x(KI_FLAG(KI_SLEADER))), 0); hv_store(h, "stat", 4, newSViv(NO_FREEBSD_4x((int)kp->ki_stat)), 0); hv_store(h, "stat_1", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 1 ? 1 : 0)), 0); hv_store(h, "stat_2", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 2 ? 1 : 0)), 0); hv_store(h, "stat_3", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 3 ? 1 : 0)), 0); hv_store(h, "stat_4", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 4 ? 1 : 0)), 0); hv_store(h, "stat_5", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 5 ? 1 : 0)), 0); hv_store(h, "stat_6", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 6 ? 1 : 0)), 0); hv_store(h, "stat_7", 6, newSViv(NO_FREEBSD_4x((int)kp->ki_stat == 7 ? 1 : 0)), 0); hv_store(h, "ocomm", 5, newSVpv(NO_FREEBSD_4x_pv(kp->ki_ocomm), 0), 0); hv_store(h, "lockname", 8, newSVpv(NO_FREEBSD_4x_pv(kp->ki_lockname), 0), 0); hv_store(h, "pri_class", 9, newSViv(NO_FREEBSD_4x(kp->ki_pri.pri_class)), 0); hv_store(h, "pri_level", 9, newSViv(NO_FREEBSD_4x(kp->ki_pri.pri_level)), 0); hv_store(h, "pri_native", 10, newSViv(NO_FREEBSD_4x(kp->ki_pri.pri_native)), 0); hv_store(h, "pri_user", 8, newSViv(NO_FREEBSD_4x(kp->ki_pri.pri_user)), 0); NO_FREEBSD_4x(rp = &kp->ki_rusage); hv_store(h, "utime", 5, newSVnv(NO_FREEBSD_4x(TIME_FRAC(rp->ru_utime))), 0); hv_store(h, "stime", 5, newSVnv(NO_FREEBSD_4x(TIME_FRAC(rp->ru_stime))), 0); hv_store(h, "time", 4, newSVnv(NO_FREEBSD_4x( TIME_FRAC(rp->ru_utime)+TIME_FRAC(rp->ru_stime))), 0); hv_store(h, "maxrss", 6, newSVnv(NO_FREEBSD_4x(rp->ru_maxrss)), 0); hv_store(h, "ixrss", 5, newSVnv(NO_FREEBSD_4x(rp->ru_ixrss)), 0); hv_store(h, "idrss", 5, newSVnv(NO_FREEBSD_4x(rp->ru_idrss)), 0); hv_store(h, "isrss", 5, newSVnv(NO_FREEBSD_4x(rp->ru_isrss)), 0); hv_store(h, "minflt", 6, newSVnv(NO_FREEBSD_4x(rp->ru_minflt)), 0); hv_store(h, "majflt", 6, newSVnv(NO_FREEBSD_4x(rp->ru_majflt)), 0); hv_store(h, "nswap", 5, newSVnv(NO_FREEBSD_4x(rp->ru_nswap)), 0); hv_store(h, "inblock", 7, newSVnv(NO_FREEBSD_4x(rp->ru_inblock)), 0); hv_store(h, "oublock", 7, newSVnv(NO_FREEBSD_4x(rp->ru_oublock)), 0); hv_store(h, "msgsnd", 6, newSVnv(NO_FREEBSD_4x(rp->ru_msgsnd)), 0); hv_store(h, "msgrcv", 6, newSVnv(NO_FREEBSD_4x(rp->ru_msgrcv)), 0); hv_store(h, "nsignals", 8, newSViv(NO_FREEBSD_4x(rp->ru_nsignals)), 0); hv_store(h, "nvcsw", 5, newSViv(NO_FREEBSD_4x(rp->ru_nvcsw)), 0); hv_store(h, "nivcsw", 6, newSViv(NO_FREEBSD_4x(rp->ru_nivcsw)), 0); /* attributes available only in FreeBSD 6.x */ hv_store(h, "emul", 4, newSVpv(NO_FREEBSD_5x_pv(kp->ki_emul), 0), 0); hv_store(h, "jid", 3, newSViv(NO_FREEBSD_5x(kp->ki_jid)), 0); hv_store(h, "numthreads", 10, newSViv(NO_FREEBSD_5x(kp->ki_numthreads)), 0); NO_FREEBSD_5x(rp = &kp->ki_rusage_ch); hv_store(h, "utime_ch", 8, newSVnv(NO_FREEBSD_5x(TIME_FRAC(rp->ru_utime))), 0); hv_store(h, "stime_ch", 8, newSVnv(NO_FREEBSD_5x(TIME_FRAC(rp->ru_stime))), 0); hv_store(h, "time_ch", 7, newSVnv(NO_FREEBSD_5x( TIME_FRAC(rp->ru_utime)+TIME_FRAC(rp->ru_stime))), 0); hv_store(h, "maxrss_ch", 9, newSVnv(NO_FREEBSD_5x(rp->ru_maxrss)), 0); hv_store(h, "ixrss_ch", 8, newSVnv(NO_FREEBSD_5x(rp->ru_ixrss)), 0); hv_store(h, "idrss_ch", 8, newSVnv(NO_FREEBSD_5x(rp->ru_idrss)), 0); hv_store(h, "isrss_ch", 8, newSVnv(NO_FREEBSD_5x(rp->ru_isrss)), 0); hv_store(h, "minflt_ch", 9, newSVnv(NO_FREEBSD_5x(rp->ru_minflt)), 0); hv_store(h, "majflt_ch", 9, newSVnv(NO_FREEBSD_5x(rp->ru_majflt)), 0); hv_store(h, "nswap_ch", 8, newSVnv(NO_FREEBSD_5x(rp->ru_nswap)), 0); hv_store(h, "inblock_ch", 10, newSVnv(NO_FREEBSD_5x(rp->ru_inblock)), 0); hv_store(h, "oublock_ch", 10, newSVnv(NO_FREEBSD_5x(rp->ru_oublock)), 0); hv_store(h, "msgsnd_ch", 9, newSVnv(NO_FREEBSD_5x(rp->ru_msgsnd)), 0); hv_store(h, "msgrcv_ch", 9, newSVnv(NO_FREEBSD_5x(rp->ru_msgrcv)), 0); hv_store(h, "nsignals_ch", 11, newSViv(NO_FREEBSD_5x(rp->ru_nsignals)), 0); hv_store(h, "nvcsw_ch", 8, newSViv(NO_FREEBSD_5x(rp->ru_nvcsw)), 0); hv_store(h, "nivcsw_ch", 9, newSViv(NO_FREEBSD_5x(rp->ru_nivcsw)), 0); return h; } MODULE = BSD::Process PACKAGE = BSD::Process PROTOTYPES: ENABLE short max_kernel_groups() CODE: #if __FreeBSD_version < 500000 RETVAL = 0; #else RETVAL = KI_NGROUPS; #endif OUTPUT: RETVAL SV * _info(int pid, int resolve) PREINIT: /* TODO: int pid should be pid_t pid */ size_t len; struct kinfo_proc ki; HV *h; CODE: /* use the sysctl approach instead of using a kernel * descriptor, makes for a bit less housekeeping. */ if (proc_info_mib[0] == -1) { len = sizeof(proc_info_mib)/sizeof(proc_info_mib[0]); if (sysctlnametomib("kern.proc.pid", proc_info_mib, &len) == -1) { warn( "kern.proc.pid is corrupt\n"); XSRETURN_UNDEF; } } proc_info_mib[3] = pid; len = sizeof(ki); if (sysctl(proc_info_mib, sizeof(proc_info_mib)/sizeof(proc_info_mib[0]), &ki, &len, NULL, 0) == -1) { /* process identified by pid has probably exited */ XSRETURN_UNDEF; } h = _procinfo( &ki, resolve ); RETVAL = newRV((SV *)h); OUTPUT: RETVAL void _list(int request, int param) PREINIT: #ifdef dXSTARG dXSTARG; #else dTARGET; #endif struct kinfo_proc *kip; kvm_t *kd; int nr; char errbuf[_POSIX2_LINE_MAX]; const char *nlistf, *memf; PPCODE: nlistf = memf = PATH_DEV_NULL; kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); kip = _proc_request(kd, request, param, &nr); if (kip) { int p; for (p = 0; p < nr; ++kip, ++p) { #if PERL_API_VERSION == 5 && PERL_VERSION == 6 EXTEND(SP,1); XPUSHi(kip->PID_FIELD); #else mPUSHi(kip->PID_FIELD); #endif } kvm_close(kd); } else { warn("kvm error in list(): %s\n", kvm_geterr(kd)); XSRETURN_UNDEF; } XSRETURN(nr); HV * _all(int resolve, int request, int param) PREINIT: struct kinfo_proc *kip; kvm_t *kd; int nr; char errbuf[_POSIX2_LINE_MAX]; char pidbuf[16]; const char *nlistf, *memf; HV *h; HV *package; HV *out; int p; CODE: nlistf = memf = PATH_DEV_NULL; kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); kip = _proc_request(kd, request, param, &nr); if (!kip) { warn("kvm error in all(): %s\n", kvm_geterr(kd)); XSRETURN_UNDEF; } out = (HV *)sv_2mortal((SV *)newHV()); package = gv_stashpv("BSD::Process", 0); RETVAL = out; for (p = 0; p < nr; ++kip, ++p) { h = _procinfo( kip, resolve ); hv_store(h, "_resolve", 8, newSViv(resolve), 0); hv_store(h, "_pid", 4, newSViv(kip->PID_FIELD), 0); sprintf( pidbuf, "%d", kip->PID_FIELD); hv_store(out, pidbuf, strlen(pidbuf), sv_bless(newRV((SV *)h), package), 0); } kvm_close(kd); OUTPUT: RETVAL