/* htop - DarwinProcess.c (C) 2015 Hisham H. Muhammad Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ #include "Process.h" #include "DarwinProcess.h" #include #include #include /*{ #include "Settings.h" #include }*/ Process* DarwinProcess_new(Settings* settings) { Process* this = calloc(sizeof(Process), 1); Object_setClass(this, Class(Process)); Process_init(this, settings); return this; } void Process_delete(Object* cast) { Process* this = (Process*) cast; Object_setClass(this, Class(Process)); Process_done((Process*)cast); // free platform-specific fields here free(this); } bool Process_isThread(Process* this) { return false; } void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now) { struct tm date; proc->starttime_ctime = ep->p_starttime.tv_sec; (void) localtime_r(&proc->starttime_ctime, &date); strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date); } char *DarwinProcessList_getCmdLine(struct kinfo_proc* k, int show_args ) { /* This function is from the old Mac version of htop. Originally from ps? */ int mib[3], argmax, nargs, c = 0; size_t size; char *procargs, *sp, *np, *cp, *retval; /* Get the maximum process arguments size. */ mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; size = sizeof( argmax ); if ( sysctl( mib, 2, &argmax, &size, NULL, 0 ) == -1 ) { goto ERROR_A; } /* Allocate space for the arguments. */ procargs = ( char * ) malloc( argmax ); if ( procargs == NULL ) { goto ERROR_A; } /* * Make a sysctl() call to get the raw argument space of the process. * The layout is documented in start.s, which is part of the Csu * project. In summary, it looks like: * * /---------------\ 0x00000000 * : : * : : * |---------------| * | argc | * |---------------| * | arg[0] | * |---------------| * : : * : : * |---------------| * | arg[argc - 1] | * |---------------| * | 0 | * |---------------| * | env[0] | * |---------------| * : : * : : * |---------------| * | env[n] | * |---------------| * | 0 | * |---------------| <-- Beginning of data returned by sysctl() is here. * | argc | * |---------------| * | exec_path | * |:::::::::::::::| * | | * | String area. | * | | * |---------------| <-- Top of stack. * : : * : : * \---------------/ 0xffffffff */ mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = k->kp_proc.p_pid; size = ( size_t ) argmax; if ( sysctl( mib, 3, procargs, &size, NULL, 0 ) == -1 ) { goto ERROR_B; } memcpy( &nargs, procargs, sizeof( nargs ) ); cp = procargs + sizeof( nargs ); /* Skip the saved exec_path. */ for ( ; cp < &procargs[size]; cp++ ) { if ( *cp == '\0' ) { /* End of exec_path reached. */ break; } } if ( cp == &procargs[size] ) { goto ERROR_B; } /* Skip trailing '\0' characters. */ for ( ; cp < &procargs[size]; cp++ ) { if ( *cp != '\0' ) { /* Beginning of first argument reached. */ break; } } if ( cp == &procargs[size] ) { goto ERROR_B; } /* Save where the argv[0] string starts. */ sp = cp; /* * Iterate through the '\0'-terminated strings and convert '\0' to ' ' * until a string is found that has a '=' character in it (or there are * no more strings in procargs). There is no way to deterministically * know where the command arguments end and the environment strings * start, which is why the '=' character is searched for as a heuristic. */ for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) { if ( *cp == '\0' ) { c++; if ( np != NULL ) { /* Convert previous '\0'. */ *np = ' '; } /* Note location of current '\0'. */ np = cp; if ( !show_args ) { /* * Don't convert '\0' characters to ' '. * However, we needed to know that the * command name was terminated, which we * now know. */ break; } } } #if 0 /* * If eflg is non-zero, continue converting '\0' characters to ' ' * characters until no more strings that look like environment settings * follow. */ if ( ( eflg != 0 ) && ( ( getuid( ) == 0 ) || ( k->kp_eproc.e_pcred.p_ruid == getuid( ) ) ) ) { for ( ; cp < &procargs[size]; cp++ ) { if ( *cp == '\0' ) { if ( np != NULL ) { if ( &np[1] == cp ) { /* * Two '\0' characters in a row. * This should normally only * happen after all the strings * have been seen, but in any * case, stop parsing. */ break; } /* Convert previous '\0'. */ *np = ' '; } /* Note location of current '\0'. */ np = cp; } } } #endif /* * sp points to the beginning of the arguments/environment string, and * np should point to the '\0' terminator for the string. */ if ( np == NULL || np == sp ) { /* Empty or unterminated string. */ goto ERROR_B; } /* Make a copy of the string. */ retval = strdup(sp); /* Clean up. */ free( procargs ); return retval; ERROR_B: free( procargs ); ERROR_A: retval = strdup(k->kp_proc.p_comm); return retval; } void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists) { struct extern_proc *ep = &ps->kp_proc; /* UNSET HERE : * * processor * user (set at ProcessList level) * nlwp * percent_cpu * percent_mem * m_size * m_resident * minflt * majflt */ /* First, the "immutable" parts */ if(!exists) { /* Set the PID/PGID/etc. */ proc->pid = ep->p_pid; proc->ppid = ps->kp_eproc.e_ppid; proc->pgrp = ps->kp_eproc.e_pgid; proc->session = 0; /* TODO Get the session id */ proc->tgid = ps->kp_eproc.e_tpgid; proc->st_uid = ps->kp_eproc.e_ucred.cr_uid; /* e_tdev = (major << 24) | (minor & 0xffffff) */ /* e_tdev == -1 for "no device" */ proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */ DarwinProcess_setStartTime(proc, ep, now); /* The command is from the old Mac htop */ char *slash; proc->comm = DarwinProcessList_getCmdLine(ps, false); slash = strrchr(proc->comm, '/'); proc->basenameOffset = (NULL != slash) ? (slash - proc->comm) : 0; } /* Mutable information */ proc->nice = ep->p_nice; proc->priority = ep->p_priority; /* Set the state */ switch(ep->p_stat) { case SIDL: proc->state = 'I'; break; case SRUN: proc->state = 'R'; break; case SSLEEP: proc->state = 'S'; break; case SSTOP: proc->state = 'T'; break; case SZOMB: proc->state = 'Z'; break; default: proc->state = '?'; break; } /* Make sure the updated flag is set */ proc->updated = true; } void DarwinProcess_setFromLibprocPidinfo(Process *proc, uint64_t total_memory, bool preExisting) { struct proc_taskinfo pti; if(sizeof(pti) == proc_pidinfo(proc->pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { proc->nlwp = pti.pti_threadnum; proc->m_size = pti.pti_virtual_size / 1024; proc->m_resident = pti.pti_resident_size / 1024; proc->majflt = pti.pti_faults; proc->percent_mem = (double)pti.pti_resident_size * 100.0 / (double)total_memory; } } void DarwinProcess_parseThreads(Process *proc, time_t now, bool preExisting) { static const size_t IDS_SZ = sizeof(uint64_t) * 2048; uint64_t *thread_ids = (uint64_t *)malloc(IDS_SZ); size_t bytes = proc_pidinfo(proc->pid, PROC_PIDLISTTHREADS, 0, thread_ids, IDS_SZ); if(0 < bytes) { size_t count = bytes / sizeof(uint64_t); proc->nlwp = count; } free(thread_ids); /* TODO Keep reusing this block */ }