125d39513SDavid van Moolenbroek /* MIB service - proc.c - functionality based on service process tables */ 225d39513SDavid van Moolenbroek /* Eventually, the CTL_PROC subtree might end up here as well. */ 325d39513SDavid van Moolenbroek 425d39513SDavid van Moolenbroek #include "mib.h" 525d39513SDavid van Moolenbroek 625d39513SDavid van Moolenbroek #include <sys/exec.h> 725d39513SDavid van Moolenbroek #include <minix/sysinfo.h> 825d39513SDavid van Moolenbroek 925d39513SDavid van Moolenbroek #include <machine/archtypes.h> 1025d39513SDavid van Moolenbroek #include "kernel/proc.h" 1125d39513SDavid van Moolenbroek #include "servers/pm/mproc.h" 1225d39513SDavid van Moolenbroek #include "servers/vfs/const.h" 1325d39513SDavid van Moolenbroek #include "servers/vfs/fproc.h" 1425d39513SDavid van Moolenbroek 1525d39513SDavid van Moolenbroek typedef struct proc ixfer_proc_t; 1625d39513SDavid van Moolenbroek typedef struct mproc ixfer_mproc_t; 1725d39513SDavid van Moolenbroek typedef struct fproc ixfer_fproc_t; 1825d39513SDavid van Moolenbroek 1925d39513SDavid van Moolenbroek static ixfer_proc_t proc_tab[NR_TASKS + NR_PROCS]; 2025d39513SDavid van Moolenbroek static ixfer_mproc_t mproc_tab[NR_PROCS]; 2125d39513SDavid van Moolenbroek static ixfer_fproc_t fproc_tab[NR_PROCS]; 2225d39513SDavid van Moolenbroek 2325d39513SDavid van Moolenbroek /* 2425d39513SDavid van Moolenbroek * The number of processes added to the current number of processes when doing 2525d39513SDavid van Moolenbroek * a size estimation, so that the actual data retrieval does not end up with 2625d39513SDavid van Moolenbroek * too little space if new processes have forked between the two calls. We do 2725d39513SDavid van Moolenbroek * a process table update only once per clock tick, which means that typically 2825d39513SDavid van Moolenbroek * no update will take place between the user process's size estimation request 2925d39513SDavid van Moolenbroek * and its subsequent data retrieval request. On the other hand, if we do 3025d39513SDavid van Moolenbroek * update process tables in between, quite a bit might have changed. 3125d39513SDavid van Moolenbroek */ 3225d39513SDavid van Moolenbroek #define EXTRA_PROCS 8 3325d39513SDavid van Moolenbroek 3425d39513SDavid van Moolenbroek #define HASH_SLOTS (NR_PROCS / 4) /* expected nr. of processes in use */ 3525d39513SDavid van Moolenbroek #define NO_SLOT (-1) 3625d39513SDavid van Moolenbroek static int hash_tab[HASH_SLOTS]; /* hash table mapping from PID.. */ 3725d39513SDavid van Moolenbroek static int hnext_tab[NR_PROCS]; /* ..to PM process slot */ 3825d39513SDavid van Moolenbroek 3925d39513SDavid van Moolenbroek static clock_t tabs_updated = 0; /* when the tables were last updated */ 4025d39513SDavid van Moolenbroek static int tabs_valid = TRUE; /* FALSE if obtaining tables failed */ 4125d39513SDavid van Moolenbroek 4225d39513SDavid van Moolenbroek /* 4325d39513SDavid van Moolenbroek * Update the process tables by pulling in new copies from the kernel, PM, and 4425d39513SDavid van Moolenbroek * VFS, but only every so often and only if it has not failed before. Return 4525d39513SDavid van Moolenbroek * TRUE iff the tables are now valid. 4625d39513SDavid van Moolenbroek */ 4725d39513SDavid van Moolenbroek static int 4825d39513SDavid van Moolenbroek update_tables(void) 4925d39513SDavid van Moolenbroek { 5025d39513SDavid van Moolenbroek clock_t now; 5125d39513SDavid van Moolenbroek pid_t pid; 5225d39513SDavid van Moolenbroek int r, kslot, mslot, hslot; 5325d39513SDavid van Moolenbroek 5425d39513SDavid van Moolenbroek /* 5525d39513SDavid van Moolenbroek * If retrieving the tables failed at some point, do not keep trying 5625d39513SDavid van Moolenbroek * all the time. Such a failure is very unlikely to be transient. 5725d39513SDavid van Moolenbroek */ 5825d39513SDavid van Moolenbroek if (tabs_valid == FALSE) 5925d39513SDavid van Moolenbroek return FALSE; 6025d39513SDavid van Moolenbroek 6125d39513SDavid van Moolenbroek /* 6225d39513SDavid van Moolenbroek * Update the tables once per clock tick at most. The update operation 6325d39513SDavid van Moolenbroek * is rather heavy, transferring several hundreds of kilobytes between 6425d39513SDavid van Moolenbroek * servers. Userland should be able to live with information that is 6525d39513SDavid van Moolenbroek * outdated by at most one clock tick. 6625d39513SDavid van Moolenbroek */ 6725d39513SDavid van Moolenbroek now = getticks(); 6825d39513SDavid van Moolenbroek 6925d39513SDavid van Moolenbroek if (tabs_updated != 0 && tabs_updated == now) 7025d39513SDavid van Moolenbroek return TRUE; 7125d39513SDavid van Moolenbroek 7225d39513SDavid van Moolenbroek /* Perform an actual update now. */ 7325d39513SDavid van Moolenbroek tabs_valid = FALSE; 7425d39513SDavid van Moolenbroek 7525d39513SDavid van Moolenbroek /* Retrieve and check the kernel process table. */ 7625d39513SDavid van Moolenbroek if ((r = sys_getproctab(proc_tab)) != OK) { 7725d39513SDavid van Moolenbroek printf("MIB: unable to obtain kernel process table (%d)\n", r); 7825d39513SDavid van Moolenbroek 7925d39513SDavid van Moolenbroek return FALSE; 8025d39513SDavid van Moolenbroek } 8125d39513SDavid van Moolenbroek 8225d39513SDavid van Moolenbroek for (kslot = 0; kslot < NR_TASKS + NR_PROCS; kslot++) { 8325d39513SDavid van Moolenbroek if (proc_tab[kslot].p_magic != PMAGIC) { 8425d39513SDavid van Moolenbroek printf("MIB: kernel process table mismatch\n"); 8525d39513SDavid van Moolenbroek 8625d39513SDavid van Moolenbroek return FALSE; 8725d39513SDavid van Moolenbroek } 8825d39513SDavid van Moolenbroek } 8925d39513SDavid van Moolenbroek 9025d39513SDavid van Moolenbroek /* Retrieve and check the PM process table. */ 9125d39513SDavid van Moolenbroek r = getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc_tab, sizeof(mproc_tab)); 9225d39513SDavid van Moolenbroek if (r != OK) { 9325d39513SDavid van Moolenbroek printf("MIB: unable to obtain PM process table (%d)\n", r); 9425d39513SDavid van Moolenbroek 9525d39513SDavid van Moolenbroek return FALSE; 9625d39513SDavid van Moolenbroek } 9725d39513SDavid van Moolenbroek 9825d39513SDavid van Moolenbroek for (mslot = 0; mslot < NR_PROCS; mslot++) { 9925d39513SDavid van Moolenbroek if (mproc_tab[mslot].mp_magic != MP_MAGIC) { 10025d39513SDavid van Moolenbroek printf("MIB: PM process table mismatch\n"); 10125d39513SDavid van Moolenbroek 10225d39513SDavid van Moolenbroek return FALSE; 10325d39513SDavid van Moolenbroek } 10425d39513SDavid van Moolenbroek } 10525d39513SDavid van Moolenbroek 10625d39513SDavid van Moolenbroek /* Retrieve the VFS process table, which has no magic number. */ 10725d39513SDavid van Moolenbroek r = getsysinfo(VFS_PROC_NR, SI_PROC_TAB, fproc_tab, sizeof(fproc_tab)); 10825d39513SDavid van Moolenbroek if (r != OK) { 10925d39513SDavid van Moolenbroek printf("MIB: unable to obtain VFS process table (%d)\n", r); 11025d39513SDavid van Moolenbroek 11125d39513SDavid van Moolenbroek return FALSE; 11225d39513SDavid van Moolenbroek } 11325d39513SDavid van Moolenbroek 11425d39513SDavid van Moolenbroek tabs_valid = TRUE; 11525d39513SDavid van Moolenbroek tabs_updated = now; 11625d39513SDavid van Moolenbroek 11725d39513SDavid van Moolenbroek /* 11825d39513SDavid van Moolenbroek * Build a hash table mapping from process IDs to slot numbers, for 11925d39513SDavid van Moolenbroek * fast access. TODO: decide if this is better done on demand only. 12025d39513SDavid van Moolenbroek */ 12125d39513SDavid van Moolenbroek for (hslot = 0; hslot < HASH_SLOTS; hslot++) 12225d39513SDavid van Moolenbroek hash_tab[hslot] = NO_SLOT; 12325d39513SDavid van Moolenbroek 12425d39513SDavid van Moolenbroek for (mslot = 0; mslot < NR_PROCS; mslot++) { 12525d39513SDavid van Moolenbroek if (mproc_tab[mslot].mp_flags & IN_USE) { 12625d39513SDavid van Moolenbroek if ((pid = mproc_tab[mslot].mp_pid) <= 0) 12725d39513SDavid van Moolenbroek continue; 12825d39513SDavid van Moolenbroek 12925d39513SDavid van Moolenbroek hslot = mproc_tab[mslot].mp_pid % HASH_SLOTS; 13025d39513SDavid van Moolenbroek 13125d39513SDavid van Moolenbroek hnext_tab[mslot] = hash_tab[hslot]; 13225d39513SDavid van Moolenbroek hash_tab[hslot] = mslot; 13325d39513SDavid van Moolenbroek } 13425d39513SDavid van Moolenbroek } 13525d39513SDavid van Moolenbroek 13625d39513SDavid van Moolenbroek return TRUE; 13725d39513SDavid van Moolenbroek } 13825d39513SDavid van Moolenbroek 13925d39513SDavid van Moolenbroek /* 14025d39513SDavid van Moolenbroek * Return the PM slot number for the given PID, or NO_SLOT if the PID is not in 14125d39513SDavid van Moolenbroek * use by a process. 14225d39513SDavid van Moolenbroek */ 14325d39513SDavid van Moolenbroek static int 14425d39513SDavid van Moolenbroek get_mslot(pid_t pid) 14525d39513SDavid van Moolenbroek { 14625d39513SDavid van Moolenbroek int mslot; 14725d39513SDavid van Moolenbroek 14825d39513SDavid van Moolenbroek /* PID 0 identifies the kernel; checking this is up to the caller. */ 14925d39513SDavid van Moolenbroek if (pid <= 0) 15025d39513SDavid van Moolenbroek return NO_SLOT; 15125d39513SDavid van Moolenbroek 15225d39513SDavid van Moolenbroek for (mslot = hash_tab[pid % HASH_SLOTS]; mslot != NO_SLOT; 15325d39513SDavid van Moolenbroek mslot = hnext_tab[mslot]) 15425d39513SDavid van Moolenbroek if (mproc_tab[mslot].mp_pid == pid) 15525d39513SDavid van Moolenbroek break; 15625d39513SDavid van Moolenbroek 15725d39513SDavid van Moolenbroek return mslot; 15825d39513SDavid van Moolenbroek } 15925d39513SDavid van Moolenbroek 16025d39513SDavid van Moolenbroek /* 16125d39513SDavid van Moolenbroek * Store the given number of clock ticks as a timeval structure. 16225d39513SDavid van Moolenbroek */ 16325d39513SDavid van Moolenbroek static void 16425d39513SDavid van Moolenbroek ticks_to_timeval(struct timeval * tv, clock_t ticks) 16525d39513SDavid van Moolenbroek { 16625d39513SDavid van Moolenbroek clock_t hz; 16725d39513SDavid van Moolenbroek 16825d39513SDavid van Moolenbroek hz = sys_hz(); 16925d39513SDavid van Moolenbroek 17025d39513SDavid van Moolenbroek tv->tv_sec = ticks / hz; 17125d39513SDavid van Moolenbroek tv->tv_usec = (long)((ticks % hz) * 1000000ULL / hz); 17225d39513SDavid van Moolenbroek } 17325d39513SDavid van Moolenbroek 17425d39513SDavid van Moolenbroek /* 17525d39513SDavid van Moolenbroek * Generate a wchan message text for the cases that the process is blocked on 17625d39513SDavid van Moolenbroek * IPC with another process, of which the endpoint is given as 'endpt' here. 17725d39513SDavid van Moolenbroek * The name of the other process is to be stored in 'wmesg', which is a buffer 17825d39513SDavid van Moolenbroek * of size 'wmsz'. The result should be null terminated. If 'ipc' is set, the 17925d39513SDavid van Moolenbroek * process is blocked on a direct IPC call, in which case the name of the other 18025d39513SDavid van Moolenbroek * process is enclosed in parentheses. If 'ipc' is not set, the call is made 18125d39513SDavid van Moolenbroek * indirectly through VFS, and the name of the other process should not be 18225d39513SDavid van Moolenbroek * enclosed in parentheses. If no name can be obtained, we use the endpoint of 18325d39513SDavid van Moolenbroek * the other process instead. 18425d39513SDavid van Moolenbroek */ 18525d39513SDavid van Moolenbroek static void 18625d39513SDavid van Moolenbroek fill_wmesg(char * wmesg, size_t wmsz, endpoint_t endpt, int ipc) 18725d39513SDavid van Moolenbroek { 18825d39513SDavid van Moolenbroek const char *name; 18925d39513SDavid van Moolenbroek int mslot; 19025d39513SDavid van Moolenbroek 19125d39513SDavid van Moolenbroek switch (endpt) { 19225d39513SDavid van Moolenbroek case ANY: 19325d39513SDavid van Moolenbroek name = "any"; 19425d39513SDavid van Moolenbroek break; 19525d39513SDavid van Moolenbroek case SELF: 19625d39513SDavid van Moolenbroek name = "self"; 19725d39513SDavid van Moolenbroek break; 19825d39513SDavid van Moolenbroek case NONE: 19925d39513SDavid van Moolenbroek name = "none"; 20025d39513SDavid van Moolenbroek break; 20125d39513SDavid van Moolenbroek default: 20225d39513SDavid van Moolenbroek mslot = _ENDPOINT_P(endpt); 20325d39513SDavid van Moolenbroek if (mslot >= -NR_TASKS && mslot < NR_PROCS && 20425d39513SDavid van Moolenbroek (mslot < 0 || (mproc_tab[mslot].mp_flags & IN_USE))) 20525d39513SDavid van Moolenbroek name = proc_tab[NR_TASKS + mslot].p_name; 20625d39513SDavid van Moolenbroek else 20725d39513SDavid van Moolenbroek name = NULL; 20825d39513SDavid van Moolenbroek } 20925d39513SDavid van Moolenbroek 21025d39513SDavid van Moolenbroek if (name != NULL) 21125d39513SDavid van Moolenbroek snprintf(wmesg, wmsz, "%s%s%s", 21225d39513SDavid van Moolenbroek ipc ? "(" : "", name, ipc ? ")" : ""); 21325d39513SDavid van Moolenbroek else 21425d39513SDavid van Moolenbroek snprintf(wmesg, wmsz, "%s%d%s", 21525d39513SDavid van Moolenbroek ipc ? "(" : "", endpt, ipc ? ")" : ""); 21625d39513SDavid van Moolenbroek } 21725d39513SDavid van Moolenbroek 21825d39513SDavid van Moolenbroek /* 21925d39513SDavid van Moolenbroek * Return the LWP status of a process, along with additional information in 22025d39513SDavid van Moolenbroek * case the process is sleeping (LSSLEEP): a wchan value and text to indicate 22125d39513SDavid van Moolenbroek * what the process is sleeping on, and possibly a flag field modification to 22225d39513SDavid van Moolenbroek * indicate that the sleep is interruptible. 22325d39513SDavid van Moolenbroek */ 22425d39513SDavid van Moolenbroek static int 22525d39513SDavid van Moolenbroek get_lwp_stat(int mslot, uint64_t * wcptr, char * wmptr, size_t wmsz, 22625d39513SDavid van Moolenbroek int32_t * flag) 22725d39513SDavid van Moolenbroek { 22825d39513SDavid van Moolenbroek struct mproc *mp; 22925d39513SDavid van Moolenbroek struct fproc *fp; 23025d39513SDavid van Moolenbroek struct proc *kp; 23125d39513SDavid van Moolenbroek const char *wmesg; 23225d39513SDavid van Moolenbroek uint64_t wchan; 23325d39513SDavid van Moolenbroek endpoint_t endpt; 23425d39513SDavid van Moolenbroek 23525d39513SDavid van Moolenbroek mp = &mproc_tab[mslot]; 23625d39513SDavid van Moolenbroek fp = &fproc_tab[mslot]; 23725d39513SDavid van Moolenbroek kp = &proc_tab[NR_TASKS + mslot]; 23825d39513SDavid van Moolenbroek 23925d39513SDavid van Moolenbroek /* 24025d39513SDavid van Moolenbroek * First cover all the cases that the process is not sleeping. In 24125d39513SDavid van Moolenbroek * those cases, we need not return additional sleep information either. 24225d39513SDavid van Moolenbroek */ 24325d39513SDavid van Moolenbroek if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)) 24425d39513SDavid van Moolenbroek return LSZOMB; 24525d39513SDavid van Moolenbroek 24625d39513SDavid van Moolenbroek if (mp->mp_flags & EXITING) 24725d39513SDavid van Moolenbroek return LSDEAD; 24825d39513SDavid van Moolenbroek 24925d39513SDavid van Moolenbroek if ((mp->mp_flags & TRACE_STOPPED) || RTS_ISSET(kp, RTS_P_STOP)) 25025d39513SDavid van Moolenbroek return LSSTOP; 25125d39513SDavid van Moolenbroek 25225d39513SDavid van Moolenbroek if (proc_is_runnable(kp)) 25325d39513SDavid van Moolenbroek return LSRUN; 25425d39513SDavid van Moolenbroek 25525d39513SDavid van Moolenbroek /* 25625d39513SDavid van Moolenbroek * The process is sleeping. In that case, we must also figure out why, 25725d39513SDavid van Moolenbroek * and return an appropriate wchan value and human-readable wmesg text. 25825d39513SDavid van Moolenbroek * 25925d39513SDavid van Moolenbroek * The process can be blocked on either a known sleep state in PM or 26025d39513SDavid van Moolenbroek * VFS, or otherwise on IPC communication with another process, or 26125d39513SDavid van Moolenbroek * otherwise on a kernel RTS flag. In each case, decide what to use as 26225d39513SDavid van Moolenbroek * wchan value and wmesg text, and whether the sleep is interruptible. 26325d39513SDavid van Moolenbroek * 26425d39513SDavid van Moolenbroek * The wchan value should be unique for the sleep reason. We use its 26525d39513SDavid van Moolenbroek * lower eight bits to indicate a class: 26625d39513SDavid van Moolenbroek * 0x00 = kernel task 26725d39513SDavid van Moolenbroek * 0x01 = kerel RTS block 26825d39513SDavid van Moolenbroek * 0x02 = PM call 26925d39513SDavid van Moolenbroek * 0x03 = VFS call 27025d39513SDavid van Moolenbroek * 0x04 = MIB call 27125d39513SDavid van Moolenbroek * 0xff = blocked on process 27225d39513SDavid van Moolenbroek * The upper bits are used for class-specific information. The actual 27325d39513SDavid van Moolenbroek * value does not really matter, as long as it is nonzero and there is 27425d39513SDavid van Moolenbroek * no overlap between the different values. 27525d39513SDavid van Moolenbroek */ 27625d39513SDavid van Moolenbroek wchan = 0; 27725d39513SDavid van Moolenbroek wmesg = NULL; 27825d39513SDavid van Moolenbroek 27925d39513SDavid van Moolenbroek /* 28025d39513SDavid van Moolenbroek * First see if the process is marked as blocked in the tables of PM or 28125d39513SDavid van Moolenbroek * VFS. Such a block reason is always an interruptible sleep. Note 28225d39513SDavid van Moolenbroek * that we do not use the kernel table at all in this case: each of the 28325d39513SDavid van Moolenbroek * three tables is consistent within itself, but not necessarily 28425d39513SDavid van Moolenbroek * consistent with any of the other tables, so we avoid internal 28525d39513SDavid van Moolenbroek * mismatches if we can. 28625d39513SDavid van Moolenbroek */ 28725d39513SDavid van Moolenbroek if (mp->mp_flags & WAITING) { 28825d39513SDavid van Moolenbroek wchan = 0x102; 28925d39513SDavid van Moolenbroek wmesg = "wait"; 29025d39513SDavid van Moolenbroek } else if (mp->mp_flags & SIGSUSPENDED) { 29125d39513SDavid van Moolenbroek wchan = 0x202; 29225d39513SDavid van Moolenbroek wmesg = "pause"; 29325d39513SDavid van Moolenbroek } else if (fp->fp_blocked_on != FP_BLOCKED_ON_NONE) { 29425d39513SDavid van Moolenbroek wchan = (fp->fp_blocked_on << 8) | 0x03; 29525d39513SDavid van Moolenbroek switch (fp->fp_blocked_on) { 29625d39513SDavid van Moolenbroek case FP_BLOCKED_ON_PIPE: 29725d39513SDavid van Moolenbroek wmesg = "pipe"; 29825d39513SDavid van Moolenbroek break; 29925d39513SDavid van Moolenbroek case FP_BLOCKED_ON_LOCK: 30025d39513SDavid van Moolenbroek wmesg = "lock"; 30125d39513SDavid van Moolenbroek break; 30225d39513SDavid van Moolenbroek case FP_BLOCKED_ON_POPEN: 30325d39513SDavid van Moolenbroek wmesg = "popen"; 30425d39513SDavid van Moolenbroek break; 30525d39513SDavid van Moolenbroek case FP_BLOCKED_ON_SELECT: 30625d39513SDavid van Moolenbroek wmesg = "select"; 30725d39513SDavid van Moolenbroek break; 30825d39513SDavid van Moolenbroek case FP_BLOCKED_ON_OTHER: 30925d39513SDavid van Moolenbroek /* 31025d39513SDavid van Moolenbroek * Add the task (= character driver) endpoint to the 31125d39513SDavid van Moolenbroek * wchan value, and use the driver's process name, 31225d39513SDavid van Moolenbroek * without parentheses, as wmesg text. 31325d39513SDavid van Moolenbroek */ 31425d39513SDavid van Moolenbroek wchan |= (uint64_t)fp->fp_task << 16; 31525d39513SDavid van Moolenbroek fill_wmesg(wmptr, wmsz, fp->fp_task, FALSE /*ipc*/); 31625d39513SDavid van Moolenbroek break; 31725d39513SDavid van Moolenbroek default: 31825d39513SDavid van Moolenbroek /* A newly added flag we don't yet know about? */ 31925d39513SDavid van Moolenbroek wmesg = "???"; 32025d39513SDavid van Moolenbroek break; 32125d39513SDavid van Moolenbroek } 32225d39513SDavid van Moolenbroek } 32325d39513SDavid van Moolenbroek if (wchan != 0) { 32425d39513SDavid van Moolenbroek *wcptr = wchan; 32525d39513SDavid van Moolenbroek if (wmesg != NULL) /* NULL means "already set" here */ 32625d39513SDavid van Moolenbroek strlcpy(wmptr, wmesg, wmsz); 32725d39513SDavid van Moolenbroek *flag |= L_SINTR; 32825d39513SDavid van Moolenbroek } 32925d39513SDavid van Moolenbroek 33025d39513SDavid van Moolenbroek /* 33125d39513SDavid van Moolenbroek * See if the process is blocked on sending or receiving. If not, then 33225d39513SDavid van Moolenbroek * use one of the kernel RTS flags as reason. 33325d39513SDavid van Moolenbroek */ 33425d39513SDavid van Moolenbroek endpt = P_BLOCKEDON(kp); 33525d39513SDavid van Moolenbroek 33625d39513SDavid van Moolenbroek switch (endpt) { 33725d39513SDavid van Moolenbroek case MIB_PROC_NR: 33825d39513SDavid van Moolenbroek /* This is really just aesthetics. */ 33925d39513SDavid van Moolenbroek wchan = 0x04; 34025d39513SDavid van Moolenbroek wmesg = "sysctl"; 34125d39513SDavid van Moolenbroek break; 34225d39513SDavid van Moolenbroek case NONE: 34325d39513SDavid van Moolenbroek /* 34425d39513SDavid van Moolenbroek * The process is not running, but also not blocked on IPC with 34525d39513SDavid van Moolenbroek * another process. This means it must be stopped on a kernel 34625d39513SDavid van Moolenbroek * RTS flag. 34725d39513SDavid van Moolenbroek */ 34825d39513SDavid van Moolenbroek wchan = ((uint64_t)kp->p_rts_flags << 8) | 0x01; 34925d39513SDavid van Moolenbroek if (RTS_ISSET(kp, RTS_PROC_STOP)) 35025d39513SDavid van Moolenbroek wmesg = "kstop"; 35125d39513SDavid van Moolenbroek else if (RTS_ISSET(kp, RTS_SIGNALED) || 35225d39513SDavid van Moolenbroek RTS_ISSET(kp, RTS_SIGNALED)) 35325d39513SDavid van Moolenbroek wmesg = "ksignal"; 35425d39513SDavid van Moolenbroek else if (RTS_ISSET(kp, RTS_NO_PRIV)) 35525d39513SDavid van Moolenbroek wmesg = "knopriv"; 35625d39513SDavid van Moolenbroek else if (RTS_ISSET(kp, RTS_PAGEFAULT) || 35725d39513SDavid van Moolenbroek RTS_ISSET(kp, RTS_VMREQTARGET)) 35825d39513SDavid van Moolenbroek wmesg = "fault"; 35925d39513SDavid van Moolenbroek else if (RTS_ISSET(kp, RTS_NO_QUANTUM)) 36025d39513SDavid van Moolenbroek wmesg = "sched"; 36125d39513SDavid van Moolenbroek else 36225d39513SDavid van Moolenbroek wmesg = "kflag"; 36325d39513SDavid van Moolenbroek break; 36425d39513SDavid van Moolenbroek case ANY: 36525d39513SDavid van Moolenbroek /* 36625d39513SDavid van Moolenbroek * If the process is blocked receiving from ANY, mark it as 36725d39513SDavid van Moolenbroek * being in an interruptible sleep. This looks nicer, even 36825d39513SDavid van Moolenbroek * though "interruptible" is not applicable to services at all. 36925d39513SDavid van Moolenbroek */ 37025d39513SDavid van Moolenbroek *flag |= L_SINTR; 37125d39513SDavid van Moolenbroek break; 37225d39513SDavid van Moolenbroek } 37325d39513SDavid van Moolenbroek 37425d39513SDavid van Moolenbroek /* 37525d39513SDavid van Moolenbroek * If at this point wchan is still zero, the process is blocked sending 37625d39513SDavid van Moolenbroek * or receiving. Use a wchan value based on the target endpoint, and 37725d39513SDavid van Moolenbroek * use "(procname)" as wmesg text. 37825d39513SDavid van Moolenbroek */ 37925d39513SDavid van Moolenbroek if (wchan == 0) { 38025d39513SDavid van Moolenbroek *wcptr = ((uint64_t)endpt << 8) | 0xff; 38125d39513SDavid van Moolenbroek fill_wmesg(wmptr, wmsz, endpt, TRUE /*ipc*/); 38225d39513SDavid van Moolenbroek } else { 38325d39513SDavid van Moolenbroek *wcptr = wchan; 38425d39513SDavid van Moolenbroek if (wmesg != NULL) /* NULL means "already set" here */ 38525d39513SDavid van Moolenbroek strlcpy(wmptr, wmesg, wmsz); 38625d39513SDavid van Moolenbroek } 38725d39513SDavid van Moolenbroek 38825d39513SDavid van Moolenbroek return LSSLEEP; 38925d39513SDavid van Moolenbroek } 39025d39513SDavid van Moolenbroek 39125d39513SDavid van Moolenbroek 39225d39513SDavid van Moolenbroek /* 39325d39513SDavid van Moolenbroek * Fill the part of a LWP structure that is common between kernel tasks and 39425d39513SDavid van Moolenbroek * user processes. Also return a CPU estimate in 'estcpu', because we generate 39525d39513SDavid van Moolenbroek * the value as a side effect here, and the LWP structure has no estcpu field. 39625d39513SDavid van Moolenbroek */ 39725d39513SDavid van Moolenbroek static void 39825d39513SDavid van Moolenbroek fill_lwp_common(struct kinfo_lwp * l, int kslot, uint32_t * estcpu) 39925d39513SDavid van Moolenbroek { 40025d39513SDavid van Moolenbroek struct proc *kp; 40125d39513SDavid van Moolenbroek struct timeval tv; 40225d39513SDavid van Moolenbroek clock_t uptime; 40325d39513SDavid van Moolenbroek uint32_t hz; 40425d39513SDavid van Moolenbroek 40525d39513SDavid van Moolenbroek kp = &proc_tab[kslot]; 40625d39513SDavid van Moolenbroek 40725d39513SDavid van Moolenbroek uptime = getticks(); 40825d39513SDavid van Moolenbroek hz = sys_hz(); 40925d39513SDavid van Moolenbroek 41025d39513SDavid van Moolenbroek /* 41125d39513SDavid van Moolenbroek * We use the process endpoint as the LWP ID. Not only does this allow 41225d39513SDavid van Moolenbroek * users to obtain process endpoints with "ps -s" (thus replacing the 41325d39513SDavid van Moolenbroek * MINIX3 ps(1)'s "ps -E"), but if we ever do implement kernel threads, 41425d39513SDavid van Moolenbroek * this is probably still going to be accurate. 41525d39513SDavid van Moolenbroek */ 41625d39513SDavid van Moolenbroek l->l_lid = kp->p_endpoint; 41725d39513SDavid van Moolenbroek 41825d39513SDavid van Moolenbroek /* 41925d39513SDavid van Moolenbroek * The time during which the process has not been swapped in or out is 42025d39513SDavid van Moolenbroek * not applicable for us, and thus, we set it to the time the process 42125d39513SDavid van Moolenbroek * has been running (in seconds). This value is relevant mostly for 42225d39513SDavid van Moolenbroek * ps(1)'s CPU usage correction for processes that have just started. 42325d39513SDavid van Moolenbroek */ 42425d39513SDavid van Moolenbroek if (kslot >= NR_TASKS) 42525d39513SDavid van Moolenbroek l->l_swtime = uptime - mproc_tab[kslot - NR_TASKS].mp_started; 42625d39513SDavid van Moolenbroek else 42725d39513SDavid van Moolenbroek l->l_swtime = uptime; 42825d39513SDavid van Moolenbroek l->l_swtime /= hz; 42925d39513SDavid van Moolenbroek 43025d39513SDavid van Moolenbroek /* 43125d39513SDavid van Moolenbroek * Sleep (dequeue) times are not maintained for kernel tasks, so 43225d39513SDavid van Moolenbroek * pretend they are never asleep (which is pretty accurate). 43325d39513SDavid van Moolenbroek */ 43425d39513SDavid van Moolenbroek if (kslot < NR_TASKS) 43525d39513SDavid van Moolenbroek l->l_slptime = 0; 43625d39513SDavid van Moolenbroek else 43725d39513SDavid van Moolenbroek l->l_slptime = (uptime - kp->p_dequeued) / hz; 43825d39513SDavid van Moolenbroek 43925d39513SDavid van Moolenbroek l->l_priority = kp->p_priority; 44025d39513SDavid van Moolenbroek l->l_usrpri = kp->p_priority; 44125d39513SDavid van Moolenbroek l->l_cpuid = kp->p_cpu; 44225d39513SDavid van Moolenbroek ticks_to_timeval(&tv, kp->p_user_time + kp->p_sys_time); 44325d39513SDavid van Moolenbroek l->l_rtime_sec = tv.tv_sec; 44425d39513SDavid van Moolenbroek l->l_rtime_usec = tv.tv_usec; 44525d39513SDavid van Moolenbroek 44625d39513SDavid van Moolenbroek /* 44725d39513SDavid van Moolenbroek * Obtain CPU usage percentages and estimates through library code 44825d39513SDavid van Moolenbroek * shared between the kernel and this service; see its source for 44925d39513SDavid van Moolenbroek * details. We note that the produced estcpu value is rather different 45025d39513SDavid van Moolenbroek * from the one produced by NetBSD, but this should not be a problem. 45125d39513SDavid van Moolenbroek */ 45225d39513SDavid van Moolenbroek l->l_pctcpu = cpuavg_getstats(&kp->p_cpuavg, &l->l_cpticks, estcpu, 45325d39513SDavid van Moolenbroek uptime, hz); 45425d39513SDavid van Moolenbroek } 45525d39513SDavid van Moolenbroek 45625d39513SDavid van Moolenbroek /* 45725d39513SDavid van Moolenbroek * Fill a LWP structure for a kernel task. Each kernel task has its own LWP, 45825d39513SDavid van Moolenbroek * and all of them have negative PIDs. 45925d39513SDavid van Moolenbroek */ 46025d39513SDavid van Moolenbroek static void 46125d39513SDavid van Moolenbroek fill_lwp_kern(struct kinfo_lwp * l, int kslot) 46225d39513SDavid van Moolenbroek { 46325d39513SDavid van Moolenbroek uint32_t estcpu; 46425d39513SDavid van Moolenbroek 46525d39513SDavid van Moolenbroek memset(l, 0, sizeof(*l)); 46625d39513SDavid van Moolenbroek 46725d39513SDavid van Moolenbroek l->l_flag = L_INMEM | L_SINTR | L_SYSTEM; 46825d39513SDavid van Moolenbroek l->l_stat = LSSLEEP; 46925d39513SDavid van Moolenbroek l->l_pid = kslot - NR_TASKS; 47025d39513SDavid van Moolenbroek 47125d39513SDavid van Moolenbroek /* 47225d39513SDavid van Moolenbroek * When showing LWP entries, ps(1) uses the process name rather than 47325d39513SDavid van Moolenbroek * the LWP name. All kernel tasks are therefore shown as "[kernel]" 47425d39513SDavid van Moolenbroek * anyway. We use the wmesg field to show the actual kernel task name. 47525d39513SDavid van Moolenbroek */ 47625d39513SDavid van Moolenbroek l->l_wchan = ((uint64_t)(l->l_pid) << 8) | 0x00; 47725d39513SDavid van Moolenbroek strlcpy(l->l_wmesg, proc_tab[kslot].p_name, sizeof(l->l_wmesg)); 47825d39513SDavid van Moolenbroek strlcpy(l->l_name, "kernel", sizeof(l->l_name)); 47925d39513SDavid van Moolenbroek 48025d39513SDavid van Moolenbroek fill_lwp_common(l, kslot, &estcpu); 48125d39513SDavid van Moolenbroek } 48225d39513SDavid van Moolenbroek 48325d39513SDavid van Moolenbroek /* 48425d39513SDavid van Moolenbroek * Fill a LWP structure for a user process. 48525d39513SDavid van Moolenbroek */ 48625d39513SDavid van Moolenbroek static void 48725d39513SDavid van Moolenbroek fill_lwp_user(struct kinfo_lwp * l, int mslot) 48825d39513SDavid van Moolenbroek { 48925d39513SDavid van Moolenbroek struct mproc *mp; 49025d39513SDavid van Moolenbroek uint32_t estcpu; 49125d39513SDavid van Moolenbroek 49225d39513SDavid van Moolenbroek memset(l, 0, sizeof(*l)); 49325d39513SDavid van Moolenbroek 49425d39513SDavid van Moolenbroek mp = &mproc_tab[mslot]; 49525d39513SDavid van Moolenbroek 49625d39513SDavid van Moolenbroek l->l_flag = L_INMEM; 49725d39513SDavid van Moolenbroek l->l_stat = get_lwp_stat(mslot, &l->l_wchan, l->l_wmesg, 49825d39513SDavid van Moolenbroek sizeof(l->l_wmesg), &l->l_flag); 49925d39513SDavid van Moolenbroek l->l_pid = mp->mp_pid; 50025d39513SDavid van Moolenbroek strlcpy(l->l_name, mp->mp_name, sizeof(l->l_name)); 50125d39513SDavid van Moolenbroek 50225d39513SDavid van Moolenbroek fill_lwp_common(l, NR_TASKS + mslot, &estcpu); 50325d39513SDavid van Moolenbroek } 50425d39513SDavid van Moolenbroek 50525d39513SDavid van Moolenbroek /* 50625d39513SDavid van Moolenbroek * Implementation of CTL_KERN KERN_LWP. 50725d39513SDavid van Moolenbroek */ 50825d39513SDavid van Moolenbroek ssize_t 50925d39513SDavid van Moolenbroek mib_kern_lwp(struct mib_call * call, struct mib_node * node __unused, 51025d39513SDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp __unused) 51125d39513SDavid van Moolenbroek { 51225d39513SDavid van Moolenbroek struct kinfo_lwp lwp; 51325d39513SDavid van Moolenbroek struct mproc *mp; 51425d39513SDavid van Moolenbroek size_t copysz; 51525d39513SDavid van Moolenbroek ssize_t off; 51625d39513SDavid van Moolenbroek pid_t pid; 51725d39513SDavid van Moolenbroek int r, elsz, elmax, kslot, mslot, last_mslot; 51825d39513SDavid van Moolenbroek 51925d39513SDavid van Moolenbroek if (call->call_namelen != 3) 52025d39513SDavid van Moolenbroek return EINVAL; 52125d39513SDavid van Moolenbroek 52225d39513SDavid van Moolenbroek pid = (pid_t)call->call_name[0]; 52325d39513SDavid van Moolenbroek elsz = call->call_name[1]; 52425d39513SDavid van Moolenbroek elmax = call->call_name[2]; /* redundant with the given oldlen.. */ 52525d39513SDavid van Moolenbroek 52625d39513SDavid van Moolenbroek if (pid < -1 || elsz <= 0 || elmax < 0) 52725d39513SDavid van Moolenbroek return EINVAL; 52825d39513SDavid van Moolenbroek 52925d39513SDavid van Moolenbroek if (!update_tables()) 53025d39513SDavid van Moolenbroek return EINVAL; 53125d39513SDavid van Moolenbroek 53225d39513SDavid van Moolenbroek off = 0; 53325d39513SDavid van Moolenbroek copysz = MIN((size_t)elsz, sizeof(lwp)); 53425d39513SDavid van Moolenbroek 53525d39513SDavid van Moolenbroek /* 53625d39513SDavid van Moolenbroek * We model kernel tasks as LWP threads of the kernel (with PID 0). 53725d39513SDavid van Moolenbroek * Modeling the kernel tasks as processes with negative PIDs, like 53825d39513SDavid van Moolenbroek * ProcFS does, conflicts with the KERN_LWP API here: a PID of -1 53925d39513SDavid van Moolenbroek * indicates that the caller wants a full listing of LWPs. 54025d39513SDavid van Moolenbroek */ 54125d39513SDavid van Moolenbroek if (pid <= 0) { 54225d39513SDavid van Moolenbroek for (kslot = 0; kslot < NR_TASKS; kslot++) { 54325d39513SDavid van Moolenbroek if (mib_inrange(oldp, off) && elmax > 0) { 54425d39513SDavid van Moolenbroek fill_lwp_kern(&lwp, kslot); 54525d39513SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, &lwp, 54625d39513SDavid van Moolenbroek copysz)) < 0) 54725d39513SDavid van Moolenbroek return r; 54825d39513SDavid van Moolenbroek elmax--; 54925d39513SDavid van Moolenbroek } 55025d39513SDavid van Moolenbroek off += elsz; 55125d39513SDavid van Moolenbroek } 55225d39513SDavid van Moolenbroek 55325d39513SDavid van Moolenbroek /* No need to add extra space here: NR_TASKS is static. */ 55425d39513SDavid van Moolenbroek if (pid == 0) 55525d39513SDavid van Moolenbroek return off; 55625d39513SDavid van Moolenbroek } 55725d39513SDavid van Moolenbroek 55825d39513SDavid van Moolenbroek /* 55925d39513SDavid van Moolenbroek * With PID 0 out of the way: the user requested the LWP for either a 56025d39513SDavid van Moolenbroek * specific user process (pid > 0), or for all processes (pid < 0). 56125d39513SDavid van Moolenbroek */ 56225d39513SDavid van Moolenbroek if (pid > 0) { 56325d39513SDavid van Moolenbroek if ((mslot = get_mslot(pid)) == NO_SLOT || 56425d39513SDavid van Moolenbroek (mproc_tab[mslot].mp_flags & (TRACE_ZOMBIE | ZOMBIE))) 56525d39513SDavid van Moolenbroek return ESRCH; 56625d39513SDavid van Moolenbroek last_mslot = mslot; 56725d39513SDavid van Moolenbroek } else { 56825d39513SDavid van Moolenbroek mslot = 0; 56925d39513SDavid van Moolenbroek last_mslot = NR_PROCS - 1; 57025d39513SDavid van Moolenbroek } 57125d39513SDavid van Moolenbroek 57225d39513SDavid van Moolenbroek for (; mslot <= last_mslot; mslot++) { 57325d39513SDavid van Moolenbroek mp = &mproc_tab[mslot]; 57425d39513SDavid van Moolenbroek 57525d39513SDavid van Moolenbroek if ((mp->mp_flags & (IN_USE | TRACE_ZOMBIE | ZOMBIE)) != 57625d39513SDavid van Moolenbroek IN_USE) 57725d39513SDavid van Moolenbroek continue; 57825d39513SDavid van Moolenbroek 57925d39513SDavid van Moolenbroek if (mib_inrange(oldp, off) && elmax > 0) { 58025d39513SDavid van Moolenbroek fill_lwp_user(&lwp, mslot); 58125d39513SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, &lwp, copysz)) < 0) 58225d39513SDavid van Moolenbroek return r; 58325d39513SDavid van Moolenbroek elmax--; 58425d39513SDavid van Moolenbroek } 58525d39513SDavid van Moolenbroek off += elsz; 58625d39513SDavid van Moolenbroek } 58725d39513SDavid van Moolenbroek 58825d39513SDavid van Moolenbroek if (oldp == NULL && pid < 0) 58925d39513SDavid van Moolenbroek off += EXTRA_PROCS * elsz; 59025d39513SDavid van Moolenbroek 59125d39513SDavid van Moolenbroek return off; 59225d39513SDavid van Moolenbroek } 59325d39513SDavid van Moolenbroek 59425d39513SDavid van Moolenbroek 59525d39513SDavid van Moolenbroek /* 59625d39513SDavid van Moolenbroek * Fill the part of a process structure that is common between kernel tasks and 59725d39513SDavid van Moolenbroek * user processes. 59825d39513SDavid van Moolenbroek */ 59925d39513SDavid van Moolenbroek static void 60025d39513SDavid van Moolenbroek fill_proc2_common(struct kinfo_proc2 * p, int kslot) 60125d39513SDavid van Moolenbroek { 60225d39513SDavid van Moolenbroek struct vm_usage_info vui; 60325d39513SDavid van Moolenbroek struct timeval tv; 60425d39513SDavid van Moolenbroek struct proc *kp; 60525d39513SDavid van Moolenbroek struct kinfo_lwp l; 60625d39513SDavid van Moolenbroek 60725d39513SDavid van Moolenbroek kp = &proc_tab[kslot]; 60825d39513SDavid van Moolenbroek 60925d39513SDavid van Moolenbroek /* 61025d39513SDavid van Moolenbroek * Much of the information in the LWP structure also ends up in the 61125d39513SDavid van Moolenbroek * process structure. In order to avoid duplication of some important 61225d39513SDavid van Moolenbroek * code, first generate LWP values and then copy it them into the 61325d39513SDavid van Moolenbroek * process structure. 61425d39513SDavid van Moolenbroek */ 61525d39513SDavid van Moolenbroek memset(&l, 0, sizeof(l)); 61625d39513SDavid van Moolenbroek fill_lwp_common(&l, kslot, &p->p_estcpu); 61725d39513SDavid van Moolenbroek 61825d39513SDavid van Moolenbroek /* Obtain memory usage information from VM. Ignore failures. */ 61925d39513SDavid van Moolenbroek memset(&vui, 0, sizeof(vui)); 62025d39513SDavid van Moolenbroek (void)vm_info_usage(kp->p_endpoint, &vui); 62125d39513SDavid van Moolenbroek 62225d39513SDavid van Moolenbroek ticks_to_timeval(&tv, kp->p_user_time + kp->p_sys_time); 62325d39513SDavid van Moolenbroek p->p_rtime_sec = l.l_rtime_sec; 62425d39513SDavid van Moolenbroek p->p_rtime_usec = l.l_rtime_usec; 62525d39513SDavid van Moolenbroek p->p_cpticks = l.l_cpticks; 62625d39513SDavid van Moolenbroek p->p_pctcpu = l.l_pctcpu; 62725d39513SDavid van Moolenbroek p->p_swtime = l.l_swtime; 62825d39513SDavid van Moolenbroek p->p_slptime = l.l_slptime; 62925d39513SDavid van Moolenbroek p->p_uticks = kp->p_user_time; 63025d39513SDavid van Moolenbroek p->p_sticks = kp->p_sys_time; 63125d39513SDavid van Moolenbroek /* TODO: p->p_iticks */ 63225d39513SDavid van Moolenbroek ticks_to_timeval(&tv, kp->p_user_time); 63325d39513SDavid van Moolenbroek p->p_uutime_sec = tv.tv_sec; 63425d39513SDavid van Moolenbroek p->p_uutime_usec = tv.tv_usec; 63525d39513SDavid van Moolenbroek ticks_to_timeval(&tv, kp->p_sys_time); 63625d39513SDavid van Moolenbroek p->p_ustime_sec = tv.tv_sec; 63725d39513SDavid van Moolenbroek p->p_ustime_usec = tv.tv_usec; 63825d39513SDavid van Moolenbroek 63925d39513SDavid van Moolenbroek p->p_priority = l.l_priority; 64025d39513SDavid van Moolenbroek p->p_usrpri = l.l_usrpri; 64125d39513SDavid van Moolenbroek 64225d39513SDavid van Moolenbroek p->p_vm_rssize = howmany(vui.vui_total, PAGE_SIZE); 64325d39513SDavid van Moolenbroek p->p_vm_vsize = howmany(vui.vui_virtual, PAGE_SIZE); 64425d39513SDavid van Moolenbroek p->p_vm_msize = howmany(vui.vui_mvirtual, PAGE_SIZE); 64525d39513SDavid van Moolenbroek 64625d39513SDavid van Moolenbroek p->p_uru_maxrss = vui.vui_maxrss; 64725d39513SDavid van Moolenbroek p->p_uru_minflt = vui.vui_minflt; 64825d39513SDavid van Moolenbroek p->p_uru_majflt = vui.vui_majflt; 64925d39513SDavid van Moolenbroek 65025d39513SDavid van Moolenbroek p->p_cpuid = l.l_cpuid; 65125d39513SDavid van Moolenbroek } 65225d39513SDavid van Moolenbroek 65325d39513SDavid van Moolenbroek /* 65425d39513SDavid van Moolenbroek * Fill a process structure for the kernel pseudo-process (with PID 0). 65525d39513SDavid van Moolenbroek */ 65625d39513SDavid van Moolenbroek static void 65725d39513SDavid van Moolenbroek fill_proc2_kern(struct kinfo_proc2 * p) 65825d39513SDavid van Moolenbroek { 65925d39513SDavid van Moolenbroek 66025d39513SDavid van Moolenbroek memset(p, 0, sizeof(*p)); 66125d39513SDavid van Moolenbroek 66225d39513SDavid van Moolenbroek p->p_flag = L_INMEM | L_SYSTEM | L_SINTR; 66325d39513SDavid van Moolenbroek p->p_pid = 0; 66425d39513SDavid van Moolenbroek p->p_stat = LSSLEEP; 66525d39513SDavid van Moolenbroek p->p_nice = NZERO; 66625d39513SDavid van Moolenbroek 66725d39513SDavid van Moolenbroek /* Use the KERNEL task wchan, for consistency between ps and top. */ 66825d39513SDavid van Moolenbroek p->p_wchan = ((uint64_t)KERNEL << 8) | 0x00; 66925d39513SDavid van Moolenbroek strlcpy(p->p_wmesg, "kernel", sizeof(p->p_wmesg)); 67025d39513SDavid van Moolenbroek 67125d39513SDavid van Moolenbroek strlcpy(p->p_comm, "kernel", sizeof(p->p_comm)); 67225d39513SDavid van Moolenbroek p->p_realflag = P_INMEM | P_SYSTEM | P_SINTR; 67325d39513SDavid van Moolenbroek p->p_realstat = SACTIVE; 67425d39513SDavid van Moolenbroek p->p_nlwps = NR_TASKS; 67525d39513SDavid van Moolenbroek 67625d39513SDavid van Moolenbroek /* 67725d39513SDavid van Moolenbroek * By using the KERNEL slot here, the kernel process will get a proper 67825d39513SDavid van Moolenbroek * CPU usage average. 67925d39513SDavid van Moolenbroek */ 68025d39513SDavid van Moolenbroek fill_proc2_common(p, KERNEL + NR_TASKS); 68125d39513SDavid van Moolenbroek } 68225d39513SDavid van Moolenbroek 68325d39513SDavid van Moolenbroek /* 68425d39513SDavid van Moolenbroek * Fill a process structure for a user process. 68525d39513SDavid van Moolenbroek */ 68625d39513SDavid van Moolenbroek static void 68725d39513SDavid van Moolenbroek fill_proc2_user(struct kinfo_proc2 * p, int mslot) 68825d39513SDavid van Moolenbroek { 68925d39513SDavid van Moolenbroek struct mproc *mp; 69025d39513SDavid van Moolenbroek struct fproc *fp; 69125d39513SDavid van Moolenbroek time_t boottime; 69225d39513SDavid van Moolenbroek dev_t tty; 69325d39513SDavid van Moolenbroek struct timeval tv; 69425d39513SDavid van Moolenbroek int i, r, kslot, zombie; 69525d39513SDavid van Moolenbroek 69625d39513SDavid van Moolenbroek memset(p, 0, sizeof(*p)); 69725d39513SDavid van Moolenbroek 69825d39513SDavid van Moolenbroek if ((r = getuptime(NULL, NULL, &boottime)) != OK) 69925d39513SDavid van Moolenbroek panic("getuptime failed: %d", r); 70025d39513SDavid van Moolenbroek 70125d39513SDavid van Moolenbroek kslot = NR_TASKS + mslot; 70225d39513SDavid van Moolenbroek mp = &mproc_tab[mslot]; 70325d39513SDavid van Moolenbroek fp = &fproc_tab[mslot]; 70425d39513SDavid van Moolenbroek 70525d39513SDavid van Moolenbroek zombie = (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)); 70625d39513SDavid van Moolenbroek tty = (!zombie) ? fp->fp_tty : NO_DEV; 70725d39513SDavid van Moolenbroek 70825d39513SDavid van Moolenbroek p->p_eflag = 0; 70925d39513SDavid van Moolenbroek if (tty != NO_DEV) 71025d39513SDavid van Moolenbroek p->p_eflag |= EPROC_CTTY; 71125d39513SDavid van Moolenbroek if (mp->mp_pid == mp->mp_procgrp) /* TODO: job control support */ 71225d39513SDavid van Moolenbroek p->p_eflag |= EPROC_SLEADER; 71325d39513SDavid van Moolenbroek 71425d39513SDavid van Moolenbroek p->p_exitsig = SIGCHLD; /* TODO */ 71525d39513SDavid van Moolenbroek 71625d39513SDavid van Moolenbroek p->p_flag = P_INMEM; 71725d39513SDavid van Moolenbroek if (mp->mp_flags & TAINTED) 71825d39513SDavid van Moolenbroek p->p_flag |= P_SUGID; 71925d39513SDavid van Moolenbroek if (mp->mp_tracer != NO_TRACER) 72025d39513SDavid van Moolenbroek p->p_flag |= P_TRACED; 72125d39513SDavid van Moolenbroek if (tty != NO_DEV) 72225d39513SDavid van Moolenbroek p->p_flag |= P_CONTROLT; 72325d39513SDavid van Moolenbroek p->p_pid = mp->mp_pid; 72425d39513SDavid van Moolenbroek if (mp->mp_parent >= 0 && mp->mp_parent < NR_PROCS) 72525d39513SDavid van Moolenbroek p->p_ppid = mproc_tab[mp->mp_parent].mp_pid; 72625d39513SDavid van Moolenbroek p->p_sid = mp->mp_procgrp; /* TODO: job control supported */ 72725d39513SDavid van Moolenbroek p->p__pgid = mp->mp_procgrp; 72825d39513SDavid van Moolenbroek p->p_tpgid = (tty != NO_DEV) ? mp->mp_procgrp : 0; 72925d39513SDavid van Moolenbroek p->p_uid = mp->mp_effuid; 73025d39513SDavid van Moolenbroek p->p_ruid = mp->mp_realuid; 73125d39513SDavid van Moolenbroek p->p_gid = mp->mp_effgid; 73225d39513SDavid van Moolenbroek p->p_rgid = mp->mp_realgid; 73325d39513SDavid van Moolenbroek p->p_ngroups = MIN(mp->mp_ngroups, KI_NGROUPS); 73425d39513SDavid van Moolenbroek for (i = 0; i < p->p_ngroups; i++) 73525d39513SDavid van Moolenbroek p->p_groups[i] = mp->mp_sgroups[i]; 73625d39513SDavid van Moolenbroek p->p_tdev = tty; 73725d39513SDavid van Moolenbroek memcpy(&p->p_siglist, &mp->mp_sigpending, sizeof(p->p_siglist)); 73825d39513SDavid van Moolenbroek memcpy(&p->p_sigmask, &mp->mp_sigmask, sizeof(p->p_sigmask)); 73925d39513SDavid van Moolenbroek memcpy(&p->p_sigcatch, &mp->mp_catch, sizeof(p->p_sigcatch)); 74025d39513SDavid van Moolenbroek memcpy(&p->p_sigignore, &mp->mp_ignore, sizeof(p->p_sigignore)); 74125d39513SDavid van Moolenbroek p->p_nice = mp->mp_nice + NZERO; 74225d39513SDavid van Moolenbroek strlcpy(p->p_comm, mp->mp_name, sizeof(p->p_comm)); 74325d39513SDavid van Moolenbroek p->p_uvalid = 1; 74425d39513SDavid van Moolenbroek ticks_to_timeval(&tv, mp->mp_started); 74525d39513SDavid van Moolenbroek p->p_ustart_sec = boottime + tv.tv_sec; 74625d39513SDavid van Moolenbroek p->p_ustart_usec = tv.tv_usec; 74725d39513SDavid van Moolenbroek /* TODO: other rusage fields */ 74825d39513SDavid van Moolenbroek ticks_to_timeval(&tv, mp->mp_child_utime + mp->mp_child_stime); 74925d39513SDavid van Moolenbroek p->p_uctime_sec = tv.tv_sec; 75025d39513SDavid van Moolenbroek p->p_uctime_usec = tv.tv_usec; 75125d39513SDavid van Moolenbroek p->p_realflag = p->p_flag; 75225d39513SDavid van Moolenbroek p->p_nlwps = (zombie) ? 0 : 1; 75325d39513SDavid van Moolenbroek 75425d39513SDavid van Moolenbroek p->p_stat = get_lwp_stat(mslot, &p->p_wchan, p->p_wmesg, 75525d39513SDavid van Moolenbroek sizeof(p->p_wmesg), &p->p_flag); 75625d39513SDavid van Moolenbroek 75725d39513SDavid van Moolenbroek switch (p->p_stat) { 75825d39513SDavid van Moolenbroek case LSRUN: 75925d39513SDavid van Moolenbroek p->p_realstat = SACTIVE; 76025d39513SDavid van Moolenbroek p->p_nrlwps = 1; 76125d39513SDavid van Moolenbroek break; 76225d39513SDavid van Moolenbroek case LSSLEEP: 76325d39513SDavid van Moolenbroek p->p_realstat = SACTIVE; 76425d39513SDavid van Moolenbroek if (p->p_flag & L_SINTR) 76525d39513SDavid van Moolenbroek p->p_realflag |= P_SINTR; 76625d39513SDavid van Moolenbroek break; 76725d39513SDavid van Moolenbroek case LSSTOP: 76825d39513SDavid van Moolenbroek p->p_realstat = SSTOP; 76925d39513SDavid van Moolenbroek break; 77025d39513SDavid van Moolenbroek case LSZOMB: 77125d39513SDavid van Moolenbroek p->p_realstat = SZOMB; 77225d39513SDavid van Moolenbroek break; 77325d39513SDavid van Moolenbroek case LSDEAD: 77425d39513SDavid van Moolenbroek p->p_stat = LSZOMB; /* ps(1) STAT does not know LSDEAD */ 77525d39513SDavid van Moolenbroek p->p_realstat = SDEAD; 77625d39513SDavid van Moolenbroek break; 77725d39513SDavid van Moolenbroek default: 77825d39513SDavid van Moolenbroek assert(0); 77925d39513SDavid van Moolenbroek } 78025d39513SDavid van Moolenbroek 78125d39513SDavid van Moolenbroek if (!zombie) 78225d39513SDavid van Moolenbroek fill_proc2_common(p, kslot); 78325d39513SDavid van Moolenbroek } 78425d39513SDavid van Moolenbroek 78525d39513SDavid van Moolenbroek /* 78625d39513SDavid van Moolenbroek * Implementation of CTL_KERN KERN_PROC2. 78725d39513SDavid van Moolenbroek */ 78825d39513SDavid van Moolenbroek ssize_t 78925d39513SDavid van Moolenbroek mib_kern_proc2(struct mib_call * call, struct mib_node * node __unused, 79025d39513SDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp __unused) 79125d39513SDavid van Moolenbroek { 79225d39513SDavid van Moolenbroek struct kinfo_proc2 proc2; 79325d39513SDavid van Moolenbroek struct mproc *mp; 79425d39513SDavid van Moolenbroek size_t copysz; 79525d39513SDavid van Moolenbroek ssize_t off; 79625d39513SDavid van Moolenbroek dev_t tty; 79725d39513SDavid van Moolenbroek int r, req, arg, elsz, elmax, kmatch, zombie, mslot; 79825d39513SDavid van Moolenbroek 79925d39513SDavid van Moolenbroek if (call->call_namelen != 4) 80025d39513SDavid van Moolenbroek return EINVAL; 80125d39513SDavid van Moolenbroek 80225d39513SDavid van Moolenbroek req = call->call_name[0]; 80325d39513SDavid van Moolenbroek arg = call->call_name[1]; 80425d39513SDavid van Moolenbroek elsz = call->call_name[2]; 80525d39513SDavid van Moolenbroek elmax = call->call_name[3]; /* redundant with the given oldlen.. */ 80625d39513SDavid van Moolenbroek 80725d39513SDavid van Moolenbroek /* 80825d39513SDavid van Moolenbroek * The kernel is special, in that it does not have a slot in the PM or 80925d39513SDavid van Moolenbroek * VFS tables. As such, it is dealt with separately. While checking 81025d39513SDavid van Moolenbroek * arguments, we might as well check whether the kernel is matched. 81125d39513SDavid van Moolenbroek */ 81225d39513SDavid van Moolenbroek switch (req) { 81325d39513SDavid van Moolenbroek case KERN_PROC_ALL: 81425d39513SDavid van Moolenbroek kmatch = TRUE; 81525d39513SDavid van Moolenbroek break; 81625d39513SDavid van Moolenbroek case KERN_PROC_PID: 81725d39513SDavid van Moolenbroek case KERN_PROC_SESSION: 81825d39513SDavid van Moolenbroek case KERN_PROC_PGRP: 81925d39513SDavid van Moolenbroek case KERN_PROC_UID: 82025d39513SDavid van Moolenbroek case KERN_PROC_RUID: 82125d39513SDavid van Moolenbroek case KERN_PROC_GID: 82225d39513SDavid van Moolenbroek case KERN_PROC_RGID: 82325d39513SDavid van Moolenbroek kmatch = (arg == 0); 82425d39513SDavid van Moolenbroek break; 82525d39513SDavid van Moolenbroek case KERN_PROC_TTY: 82625d39513SDavid van Moolenbroek kmatch = ((dev_t)arg == KERN_PROC_TTY_NODEV); 82725d39513SDavid van Moolenbroek break; 82825d39513SDavid van Moolenbroek default: 82925d39513SDavid van Moolenbroek return EINVAL; 83025d39513SDavid van Moolenbroek } 83125d39513SDavid van Moolenbroek 83225d39513SDavid van Moolenbroek if (elsz <= 0 || elmax < 0) 83325d39513SDavid van Moolenbroek return EINVAL; 83425d39513SDavid van Moolenbroek 83525d39513SDavid van Moolenbroek if (!update_tables()) 83625d39513SDavid van Moolenbroek return EINVAL; 83725d39513SDavid van Moolenbroek 83825d39513SDavid van Moolenbroek off = 0; 83925d39513SDavid van Moolenbroek copysz = MIN((size_t)elsz, sizeof(proc2)); 84025d39513SDavid van Moolenbroek 84125d39513SDavid van Moolenbroek if (kmatch) { 84225d39513SDavid van Moolenbroek if (mib_inrange(oldp, off) && elmax > 0) { 84325d39513SDavid van Moolenbroek fill_proc2_kern(&proc2); 84425d39513SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, &proc2, copysz)) < 0) 84525d39513SDavid van Moolenbroek return r; 84625d39513SDavid van Moolenbroek elmax--; 84725d39513SDavid van Moolenbroek } 84825d39513SDavid van Moolenbroek off += elsz; 84925d39513SDavid van Moolenbroek } 85025d39513SDavid van Moolenbroek 85125d39513SDavid van Moolenbroek for (mslot = 0; mslot < NR_PROCS; mslot++) { 85225d39513SDavid van Moolenbroek mp = &mproc_tab[mslot]; 85325d39513SDavid van Moolenbroek 85425d39513SDavid van Moolenbroek if (!(mp->mp_flags & IN_USE)) 85525d39513SDavid van Moolenbroek continue; 85625d39513SDavid van Moolenbroek 85725d39513SDavid van Moolenbroek switch (req) { 85825d39513SDavid van Moolenbroek case KERN_PROC_PID: 85925d39513SDavid van Moolenbroek if ((pid_t)arg != mp->mp_pid) 86025d39513SDavid van Moolenbroek continue; 86125d39513SDavid van Moolenbroek break; 86225d39513SDavid van Moolenbroek case KERN_PROC_SESSION: /* TODO: job control support */ 86325d39513SDavid van Moolenbroek case KERN_PROC_PGRP: 86425d39513SDavid van Moolenbroek if ((pid_t)arg != mp->mp_procgrp) 86525d39513SDavid van Moolenbroek continue; 86625d39513SDavid van Moolenbroek break; 86725d39513SDavid van Moolenbroek case KERN_PROC_TTY: 86825d39513SDavid van Moolenbroek if ((dev_t)arg == KERN_PROC_TTY_REVOKE) 86925d39513SDavid van Moolenbroek continue; /* TODO: revoke(2) support */ 87025d39513SDavid van Moolenbroek /* Do not access the fproc_tab slot of zombies. */ 87125d39513SDavid van Moolenbroek zombie = (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)); 87225d39513SDavid van Moolenbroek tty = (zombie) ? fproc_tab[mslot].fp_tty : NO_DEV; 87325d39513SDavid van Moolenbroek if ((dev_t)arg == KERN_PROC_TTY_NODEV) { 87425d39513SDavid van Moolenbroek if (tty != NO_DEV) 87525d39513SDavid van Moolenbroek continue; 87625d39513SDavid van Moolenbroek } else if ((dev_t)arg == NO_DEV || (dev_t)arg != tty) 87725d39513SDavid van Moolenbroek continue; 87825d39513SDavid van Moolenbroek break; 87925d39513SDavid van Moolenbroek case KERN_PROC_UID: 88025d39513SDavid van Moolenbroek if ((uid_t)arg != mp->mp_effuid) 88125d39513SDavid van Moolenbroek continue; 88225d39513SDavid van Moolenbroek break; 88325d39513SDavid van Moolenbroek case KERN_PROC_RUID: 88425d39513SDavid van Moolenbroek if ((uid_t)arg != mp->mp_realuid) 88525d39513SDavid van Moolenbroek continue; 88625d39513SDavid van Moolenbroek break; 88725d39513SDavid van Moolenbroek case KERN_PROC_GID: 88825d39513SDavid van Moolenbroek if ((gid_t)arg != mp->mp_effgid) 88925d39513SDavid van Moolenbroek continue; 89025d39513SDavid van Moolenbroek break; 89125d39513SDavid van Moolenbroek case KERN_PROC_RGID: 89225d39513SDavid van Moolenbroek if ((gid_t)arg != mp->mp_realgid) 89325d39513SDavid van Moolenbroek continue; 89425d39513SDavid van Moolenbroek break; 89525d39513SDavid van Moolenbroek } 89625d39513SDavid van Moolenbroek 89725d39513SDavid van Moolenbroek if (mib_inrange(oldp, off) && elmax > 0) { 89825d39513SDavid van Moolenbroek fill_proc2_user(&proc2, mslot); 89925d39513SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, &proc2, copysz)) < 0) 90025d39513SDavid van Moolenbroek return r; 90125d39513SDavid van Moolenbroek elmax--; 90225d39513SDavid van Moolenbroek } 90325d39513SDavid van Moolenbroek off += elsz; 90425d39513SDavid van Moolenbroek } 90525d39513SDavid van Moolenbroek 90625d39513SDavid van Moolenbroek if (oldp == NULL && req != KERN_PROC_PID) 90725d39513SDavid van Moolenbroek off += EXTRA_PROCS * elsz; 90825d39513SDavid van Moolenbroek 90925d39513SDavid van Moolenbroek return off; 91025d39513SDavid van Moolenbroek } 91125d39513SDavid van Moolenbroek 91225d39513SDavid van Moolenbroek /* 91325d39513SDavid van Moolenbroek * Implementation of CTL_KERN KERN_PROC_ARGS. 91425d39513SDavid van Moolenbroek */ 91525d39513SDavid van Moolenbroek ssize_t 91625d39513SDavid van Moolenbroek mib_kern_proc_args(struct mib_call * call, struct mib_node * node __unused, 91725d39513SDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp __unused) 91825d39513SDavid van Moolenbroek { 91925d39513SDavid van Moolenbroek char vbuf[PAGE_SIZE], sbuf[PAGE_SIZE], obuf[PAGE_SIZE]; 92025d39513SDavid van Moolenbroek struct ps_strings pss; 92125d39513SDavid van Moolenbroek struct mproc *mp; 92225d39513SDavid van Moolenbroek char *buf, *p, *q, *pptr; 92325d39513SDavid van Moolenbroek vir_bytes vaddr, vpage, spage, paddr, ppage; 92425d39513SDavid van Moolenbroek size_t max, off, olen, oleft, oldlen, bytes, pleft; 92525d39513SDavid van Moolenbroek unsigned int copybudget; 92625d39513SDavid van Moolenbroek pid_t pid; 92725d39513SDavid van Moolenbroek int req, mslot, count, aborted, ended; 92825d39513SDavid van Moolenbroek ssize_t r; 92925d39513SDavid van Moolenbroek 93025d39513SDavid van Moolenbroek if (call->call_namelen != 2) 93125d39513SDavid van Moolenbroek return EINVAL; 93225d39513SDavid van Moolenbroek 93325d39513SDavid van Moolenbroek pid = call->call_name[0]; 93425d39513SDavid van Moolenbroek req = call->call_name[1]; 93525d39513SDavid van Moolenbroek 93625d39513SDavid van Moolenbroek switch (req) { 93725d39513SDavid van Moolenbroek case KERN_PROC_ARGV: 93825d39513SDavid van Moolenbroek case KERN_PROC_ENV: 93925d39513SDavid van Moolenbroek case KERN_PROC_NARGV: 94025d39513SDavid van Moolenbroek case KERN_PROC_NENV: 94125d39513SDavid van Moolenbroek break; 94225d39513SDavid van Moolenbroek default: 94325d39513SDavid van Moolenbroek return EOPNOTSUPP; 94425d39513SDavid van Moolenbroek } 94525d39513SDavid van Moolenbroek 94625d39513SDavid van Moolenbroek if (!update_tables()) 94725d39513SDavid van Moolenbroek return EINVAL; 94825d39513SDavid van Moolenbroek 94925d39513SDavid van Moolenbroek if ((mslot = get_mslot(pid)) == NO_SLOT) 95025d39513SDavid van Moolenbroek return ESRCH; 95125d39513SDavid van Moolenbroek mp = &mproc_tab[mslot]; 95225d39513SDavid van Moolenbroek if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)) 95325d39513SDavid van Moolenbroek return ESRCH; 95425d39513SDavid van Moolenbroek 95525d39513SDavid van Moolenbroek /* We can return the count field size without copying in any data. */ 95625d39513SDavid van Moolenbroek if (oldp == NULL && (req == KERN_PROC_NARGV || req == KERN_PROC_NENV)) 95725d39513SDavid van Moolenbroek return sizeof(count); 95825d39513SDavid van Moolenbroek 95925d39513SDavid van Moolenbroek if (sys_datacopy(mp->mp_endpoint, 96025d39513SDavid van Moolenbroek mp->mp_frame_addr + mp->mp_frame_len - sizeof(pss), 96125d39513SDavid van Moolenbroek SELF, (vir_bytes)&pss, sizeof(pss)) != OK) 96225d39513SDavid van Moolenbroek return EINVAL; 96325d39513SDavid van Moolenbroek 96425d39513SDavid van Moolenbroek /* 96525d39513SDavid van Moolenbroek * Determine the upper size limit of the requested data. Not only may 96625d39513SDavid van Moolenbroek * the size never exceed ARG_MAX, it may also not exceed the frame 96725d39513SDavid van Moolenbroek * length as given in its original exec call. In fact, the frame 96825d39513SDavid van Moolenbroek * length should be substantially larger: all strings for both the 96925d39513SDavid van Moolenbroek * arguments and the environment are in there, along with other stuff, 97025d39513SDavid van Moolenbroek * and there must be no overlap between strings. It is possible that 97125d39513SDavid van Moolenbroek * the application called setproctitle(3), in which case the ps_strings 97225d39513SDavid van Moolenbroek * pointers refer to data outside the frame altogether. However, this 97325d39513SDavid van Moolenbroek * data should not exceed 2048 bytes, and we cover this by rounding up 97425d39513SDavid van Moolenbroek * the frame length to a multiple of the page size. Anyhow, NetBSD 97525d39513SDavid van Moolenbroek * blindly returns ARG_MAX when asked for a size estimate, so with this 97625d39513SDavid van Moolenbroek * maximum we are already quite a bit more accurate. 97725d39513SDavid van Moolenbroek */ 97825d39513SDavid van Moolenbroek max = roundup(MIN(mp->mp_frame_len, ARG_MAX), PAGE_SIZE); 97925d39513SDavid van Moolenbroek 98025d39513SDavid van Moolenbroek switch (req) { 98125d39513SDavid van Moolenbroek case KERN_PROC_NARGV: 98225d39513SDavid van Moolenbroek count = pss.ps_nargvstr; 98325d39513SDavid van Moolenbroek return mib_copyout(oldp, 0, &count, sizeof(count)); 98425d39513SDavid van Moolenbroek case KERN_PROC_NENV: 98525d39513SDavid van Moolenbroek count = pss.ps_nenvstr; 98625d39513SDavid van Moolenbroek return mib_copyout(oldp, 0, &count, sizeof(count)); 98725d39513SDavid van Moolenbroek case KERN_PROC_ARGV: 98825d39513SDavid van Moolenbroek if (oldp == NULL) 98925d39513SDavid van Moolenbroek return max; 99025d39513SDavid van Moolenbroek vaddr = (vir_bytes)pss.ps_argvstr; 99125d39513SDavid van Moolenbroek count = pss.ps_nargvstr; 99225d39513SDavid van Moolenbroek break; 99325d39513SDavid van Moolenbroek case KERN_PROC_ENV: 99425d39513SDavid van Moolenbroek if (oldp == NULL) 99525d39513SDavid van Moolenbroek return max; 99625d39513SDavid van Moolenbroek vaddr = (vir_bytes)pss.ps_envstr; 99725d39513SDavid van Moolenbroek count = pss.ps_nenvstr; 99825d39513SDavid van Moolenbroek break; 99925d39513SDavid van Moolenbroek } 100025d39513SDavid van Moolenbroek 100125d39513SDavid van Moolenbroek /* 100225d39513SDavid van Moolenbroek * Go through the strings. Copy in entire, machine-aligned pages at 100325d39513SDavid van Moolenbroek * once, in the hope that all data is stored consecutively, which it 100425d39513SDavid van Moolenbroek * should be: we expect that the vector is followed by the strings, and 100525d39513SDavid van Moolenbroek * that the strings are stored in order of vector reference. We keep 100625d39513SDavid van Moolenbroek * up to two pages with copied-in data: one for the vector, and 100725d39513SDavid van Moolenbroek * optionally one for string data. In addition, we keep one page with 100825d39513SDavid van Moolenbroek * data to be copied out, so that we do not cause a lot of copy 100925d39513SDavid van Moolenbroek * overhead for short strings. 101025d39513SDavid van Moolenbroek * 101125d39513SDavid van Moolenbroek * We stop whenever any of the following conditions are met: 101225d39513SDavid van Moolenbroek * - copying in data from the target process fails for any reason; 101325d39513SDavid van Moolenbroek * - we have processed the last index ('count') into the vector; 101425d39513SDavid van Moolenbroek * - the current vector element is a NULL pointer; 101525d39513SDavid van Moolenbroek * - the requested number of output bytes ('oldlen') has been reached; 101625d39513SDavid van Moolenbroek * - the maximum number of output bytes ('max') has been reached; 101725d39513SDavid van Moolenbroek * - the number of page copy-ins exceeds an estimated threshold; 101825d39513SDavid van Moolenbroek * - copying out data fails for any reason (we then return the error). 101925d39513SDavid van Moolenbroek * 102025d39513SDavid van Moolenbroek * We limit the number of page copy-ins because otherwise a rogue 102125d39513SDavid van Moolenbroek * process could create an argument vector consisting of only two-byte 102225d39513SDavid van Moolenbroek * strings that all span two pages, causing us to copy up to 1GB of 102325d39513SDavid van Moolenbroek * data with the current ARG_MAX value of 256K. No reasonable vector 102425d39513SDavid van Moolenbroek * should cause more than (ARG_MAX / PAGE_SIZE) page copies for 102525d39513SDavid van Moolenbroek * strings; we are nice enough to allow twice that. Vector copies do 102625d39513SDavid van Moolenbroek * not count, as they are linear anyway. 102725d39513SDavid van Moolenbroek * 102825d39513SDavid van Moolenbroek * Unlike every other sysctl(2) call, we are supposed to truncate the 102925d39513SDavid van Moolenbroek * resulting size (the returned 'oldlen') to the requested size (the 103025d39513SDavid van Moolenbroek * given 'oldlen') *and* return the resulting size, rather than ENOMEM 103125d39513SDavid van Moolenbroek * and the real size. Unfortunately, libkvm actually relies on this. 103225d39513SDavid van Moolenbroek * 103325d39513SDavid van Moolenbroek * Generally speaking, upon failure we just return a truncated result. 103425d39513SDavid van Moolenbroek * In case of truncation, the data we copy out need not be null 103525d39513SDavid van Moolenbroek * terminated. It is up to userland to process the data correctly. 103625d39513SDavid van Moolenbroek */ 103725d39513SDavid van Moolenbroek if (trunc_page(vaddr) == 0 || vaddr % sizeof(char *) != 0) 103825d39513SDavid van Moolenbroek return 0; 103925d39513SDavid van Moolenbroek 104025d39513SDavid van Moolenbroek off = 0; 104125d39513SDavid van Moolenbroek olen = 0; 104225d39513SDavid van Moolenbroek aborted = FALSE; 104325d39513SDavid van Moolenbroek 104425d39513SDavid van Moolenbroek oldlen = mib_getoldlen(oldp); 104525d39513SDavid van Moolenbroek if (oldlen > max) 104625d39513SDavid van Moolenbroek oldlen = max; 104725d39513SDavid van Moolenbroek 104825d39513SDavid van Moolenbroek copybudget = (ARG_MAX / PAGE_SIZE) * 2; 104925d39513SDavid van Moolenbroek 105025d39513SDavid van Moolenbroek vpage = 0; 105125d39513SDavid van Moolenbroek spage = 0; 105225d39513SDavid van Moolenbroek 105325d39513SDavid van Moolenbroek while (count > 0 && off + olen < oldlen && !aborted) { 105425d39513SDavid van Moolenbroek /* 105525d39513SDavid van Moolenbroek * Start by fetching the page containing the current vector 105625d39513SDavid van Moolenbroek * element, if needed. We could limit the fetch to the vector 105725d39513SDavid van Moolenbroek * size, but our hope is that for the simple cases, the strings 105825d39513SDavid van Moolenbroek * are on the remainder of the same page, so we save a copy 105925d39513SDavid van Moolenbroek * call. TODO: since the strings should follow the vector, we 106025d39513SDavid van Moolenbroek * could start the copy at the base of the vector. 106125d39513SDavid van Moolenbroek */ 106225d39513SDavid van Moolenbroek if (trunc_page(vaddr) != vpage) { 106325d39513SDavid van Moolenbroek vpage = trunc_page(vaddr); 106425d39513SDavid van Moolenbroek if (sys_datacopy(mp->mp_endpoint, vpage, SELF, 106525d39513SDavid van Moolenbroek (vir_bytes)vbuf, PAGE_SIZE) != OK) 106625d39513SDavid van Moolenbroek break; 106725d39513SDavid van Moolenbroek } 106825d39513SDavid van Moolenbroek 106925d39513SDavid van Moolenbroek /* Get the current vector element, pointing to a string. */ 107025d39513SDavid van Moolenbroek memcpy(&pptr, &vbuf[vaddr - vpage], sizeof(pptr)); 107125d39513SDavid van Moolenbroek paddr = (vir_bytes)pptr; 107225d39513SDavid van Moolenbroek ppage = trunc_page(paddr); 107325d39513SDavid van Moolenbroek if (ppage == 0) 107425d39513SDavid van Moolenbroek break; 107525d39513SDavid van Moolenbroek 107625d39513SDavid van Moolenbroek /* Fetch the string itself, one page at a time at most. */ 107725d39513SDavid van Moolenbroek do { 107825d39513SDavid van Moolenbroek /* 107925d39513SDavid van Moolenbroek * See if the string pointer falls inside either the 108025d39513SDavid van Moolenbroek * vector page or the previously fetched string page 108125d39513SDavid van Moolenbroek * (if any). If not, fetch a string page. 108225d39513SDavid van Moolenbroek */ 108325d39513SDavid van Moolenbroek if (ppage == vpage) { 108425d39513SDavid van Moolenbroek buf = vbuf; 108525d39513SDavid van Moolenbroek } else if (ppage == spage) { 108625d39513SDavid van Moolenbroek buf = sbuf; 108725d39513SDavid van Moolenbroek } else { 108825d39513SDavid van Moolenbroek if (--copybudget == 0) { 108925d39513SDavid van Moolenbroek aborted = TRUE; 109025d39513SDavid van Moolenbroek break; 109125d39513SDavid van Moolenbroek } 109225d39513SDavid van Moolenbroek spage = ppage; 109325d39513SDavid van Moolenbroek if (sys_datacopy(mp->mp_endpoint, spage, SELF, 109425d39513SDavid van Moolenbroek (vir_bytes)sbuf, PAGE_SIZE) != OK) { 109525d39513SDavid van Moolenbroek aborted = TRUE; 109625d39513SDavid van Moolenbroek break; 109725d39513SDavid van Moolenbroek } 109825d39513SDavid van Moolenbroek buf = sbuf; 109925d39513SDavid van Moolenbroek } 110025d39513SDavid van Moolenbroek 110125d39513SDavid van Moolenbroek /* 110225d39513SDavid van Moolenbroek * We now have a string fragment in a buffer. See if 110325d39513SDavid van Moolenbroek * the string is null terminated. If not, all the data 110425d39513SDavid van Moolenbroek * up to the buffer end is part of the string, and the 110525d39513SDavid van Moolenbroek * string continues on the next page. 110625d39513SDavid van Moolenbroek */ 110725d39513SDavid van Moolenbroek p = &buf[paddr - ppage]; 110825d39513SDavid van Moolenbroek pleft = PAGE_SIZE - (paddr - ppage); 110925d39513SDavid van Moolenbroek assert(pleft > 0); 111025d39513SDavid van Moolenbroek 111125d39513SDavid van Moolenbroek if ((q = memchr(p, '\0', pleft)) != NULL) { 111225d39513SDavid van Moolenbroek bytes = (size_t)(q - p + 1); 111325d39513SDavid van Moolenbroek assert(bytes <= pleft); 111425d39513SDavid van Moolenbroek ended = TRUE; 111525d39513SDavid van Moolenbroek } else { 111625d39513SDavid van Moolenbroek bytes = pleft; 111725d39513SDavid van Moolenbroek ended = FALSE; 111825d39513SDavid van Moolenbroek } 111925d39513SDavid van Moolenbroek 112025d39513SDavid van Moolenbroek /* Limit the result to the requested length. */ 112125d39513SDavid van Moolenbroek if (off + olen + bytes > oldlen) 112225d39513SDavid van Moolenbroek bytes = oldlen - off - olen; 112325d39513SDavid van Moolenbroek 112425d39513SDavid van Moolenbroek /* 112525d39513SDavid van Moolenbroek * Add 'bytes' bytes from string pointer 'p' to the 112625d39513SDavid van Moolenbroek * output buffer, copying out its contents to userland 112725d39513SDavid van Moolenbroek * if it has filled up. 112825d39513SDavid van Moolenbroek */ 112925d39513SDavid van Moolenbroek if (olen + bytes > sizeof(obuf)) { 113025d39513SDavid van Moolenbroek oleft = sizeof(obuf) - olen; 113125d39513SDavid van Moolenbroek memcpy(&obuf[olen], p, oleft); 113225d39513SDavid van Moolenbroek 113325d39513SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, obuf, 113425d39513SDavid van Moolenbroek sizeof(obuf))) < 0) 113525d39513SDavid van Moolenbroek return r; 113625d39513SDavid van Moolenbroek off += sizeof(obuf); 113725d39513SDavid van Moolenbroek olen = 0; 113825d39513SDavid van Moolenbroek 113925d39513SDavid van Moolenbroek p += oleft; 114025d39513SDavid van Moolenbroek bytes -= oleft; 114125d39513SDavid van Moolenbroek } 114225d39513SDavid van Moolenbroek if (bytes > 0) { 114325d39513SDavid van Moolenbroek memcpy(&obuf[olen], p, bytes); 114425d39513SDavid van Moolenbroek olen += bytes; 114525d39513SDavid van Moolenbroek } 114625d39513SDavid van Moolenbroek 114725d39513SDavid van Moolenbroek /* 114825d39513SDavid van Moolenbroek * Continue as long as we have not yet found the string 114925d39513SDavid van Moolenbroek * end, and we have not yet filled the output buffer. 115025d39513SDavid van Moolenbroek */ 115125d39513SDavid van Moolenbroek paddr += pleft; 115225d39513SDavid van Moolenbroek assert(trunc_page(paddr) == paddr); 115325d39513SDavid van Moolenbroek ppage = paddr; 115425d39513SDavid van Moolenbroek } while (!ended && off + olen < oldlen); 115525d39513SDavid van Moolenbroek 115625d39513SDavid van Moolenbroek vaddr += sizeof(char *); 115725d39513SDavid van Moolenbroek count--; 115825d39513SDavid van Moolenbroek } 115925d39513SDavid van Moolenbroek 116025d39513SDavid van Moolenbroek /* Copy out any remainder of the output buffer. */ 116125d39513SDavid van Moolenbroek if (olen > 0) { 116225d39513SDavid van Moolenbroek if ((r = mib_copyout(oldp, off, obuf, olen)) < 0) 116325d39513SDavid van Moolenbroek return r; 116425d39513SDavid van Moolenbroek off += olen; 116525d39513SDavid van Moolenbroek } 116625d39513SDavid van Moolenbroek 116725d39513SDavid van Moolenbroek assert(off <= oldlen); 116825d39513SDavid van Moolenbroek return off; 116925d39513SDavid van Moolenbroek } 1170*305e366fSDavid van Moolenbroek 1171*305e366fSDavid van Moolenbroek /* 1172*305e366fSDavid van Moolenbroek * Implementation of CTL_MINIX MINIX_PROC PROC_LIST. 1173*305e366fSDavid van Moolenbroek */ 1174*305e366fSDavid van Moolenbroek ssize_t 1175*305e366fSDavid van Moolenbroek mib_minix_proc_list(struct mib_call * call __unused, 1176*305e366fSDavid van Moolenbroek struct mib_node * node __unused, struct mib_oldp * oldp, 1177*305e366fSDavid van Moolenbroek struct mib_newp * newp __unused) 1178*305e366fSDavid van Moolenbroek { 1179*305e366fSDavid van Moolenbroek struct minix_proc_list mpl[NR_PROCS]; 1180*305e366fSDavid van Moolenbroek struct minix_proc_list *mplp; 1181*305e366fSDavid van Moolenbroek struct mproc *mp; 1182*305e366fSDavid van Moolenbroek unsigned int mslot; 1183*305e366fSDavid van Moolenbroek 1184*305e366fSDavid van Moolenbroek if (oldp == NULL) 1185*305e366fSDavid van Moolenbroek return sizeof(mpl); 1186*305e366fSDavid van Moolenbroek 1187*305e366fSDavid van Moolenbroek if (!update_tables()) 1188*305e366fSDavid van Moolenbroek return EINVAL; 1189*305e366fSDavid van Moolenbroek 1190*305e366fSDavid van Moolenbroek memset(&mpl, 0, sizeof(mpl)); 1191*305e366fSDavid van Moolenbroek 1192*305e366fSDavid van Moolenbroek mplp = mpl; 1193*305e366fSDavid van Moolenbroek mp = mproc_tab; 1194*305e366fSDavid van Moolenbroek 1195*305e366fSDavid van Moolenbroek for (mslot = 0; mslot < NR_PROCS; mslot++, mplp++, mp++) { 1196*305e366fSDavid van Moolenbroek if (!(mp->mp_flags & IN_USE) || mp->mp_pid <= 0) 1197*305e366fSDavid van Moolenbroek continue; 1198*305e366fSDavid van Moolenbroek 1199*305e366fSDavid van Moolenbroek mplp->mpl_flags = MPLF_IN_USE; 1200*305e366fSDavid van Moolenbroek if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)) 1201*305e366fSDavid van Moolenbroek mplp->mpl_flags |= MPLF_ZOMBIE; 1202*305e366fSDavid van Moolenbroek mplp->mpl_pid = mp->mp_pid; 1203*305e366fSDavid van Moolenbroek mplp->mpl_uid = mp->mp_effuid; 1204*305e366fSDavid van Moolenbroek mplp->mpl_gid = mp->mp_effgid; 1205*305e366fSDavid van Moolenbroek } 1206*305e366fSDavid van Moolenbroek 1207*305e366fSDavid van Moolenbroek return mib_copyout(oldp, 0, &mpl, sizeof(mpl)); 1208*305e366fSDavid van Moolenbroek } 1209*305e366fSDavid van Moolenbroek 1210*305e366fSDavid van Moolenbroek /* 1211*305e366fSDavid van Moolenbroek * Implementation of CTL_MINIX MINIX_PROC PROC_DATA. 1212*305e366fSDavid van Moolenbroek */ 1213*305e366fSDavid van Moolenbroek ssize_t 1214*305e366fSDavid van Moolenbroek mib_minix_proc_data(struct mib_call * call, struct mib_node * node __unused, 1215*305e366fSDavid van Moolenbroek struct mib_oldp * oldp, struct mib_newp * newp __unused) 1216*305e366fSDavid van Moolenbroek { 1217*305e366fSDavid van Moolenbroek struct minix_proc_data mpd; 1218*305e366fSDavid van Moolenbroek struct proc *kp; 1219*305e366fSDavid van Moolenbroek int kslot, mslot = 0; 1220*305e366fSDavid van Moolenbroek unsigned int mflags; 1221*305e366fSDavid van Moolenbroek pid_t pid; 1222*305e366fSDavid van Moolenbroek 1223*305e366fSDavid van Moolenbroek /* 1224*305e366fSDavid van Moolenbroek * It is currently only possible to retrieve the process data for a 1225*305e366fSDavid van Moolenbroek * particular PID, which must be given as the last name component. 1226*305e366fSDavid van Moolenbroek */ 1227*305e366fSDavid van Moolenbroek if (call->call_namelen != 1) 1228*305e366fSDavid van Moolenbroek return EINVAL; 1229*305e366fSDavid van Moolenbroek 1230*305e366fSDavid van Moolenbroek pid = (pid_t)call->call_name[0]; 1231*305e366fSDavid van Moolenbroek 1232*305e366fSDavid van Moolenbroek if (!update_tables()) 1233*305e366fSDavid van Moolenbroek return EINVAL; 1234*305e366fSDavid van Moolenbroek 1235*305e366fSDavid van Moolenbroek /* 1236*305e366fSDavid van Moolenbroek * Unlike the CTL_KERN nodes, we use the ProcFS semantics here: if the 1237*305e366fSDavid van Moolenbroek * given PID is negative, it is a kernel task; otherwise, it identifies 1238*305e366fSDavid van Moolenbroek * a user process. A request for PID 0 will result in ESRCH. 1239*305e366fSDavid van Moolenbroek */ 1240*305e366fSDavid van Moolenbroek if (pid < 0) { 1241*305e366fSDavid van Moolenbroek if (pid < -NR_TASKS) 1242*305e366fSDavid van Moolenbroek return ESRCH; 1243*305e366fSDavid van Moolenbroek 1244*305e366fSDavid van Moolenbroek kslot = pid + NR_TASKS; 1245*305e366fSDavid van Moolenbroek assert(kslot < NR_TASKS); 1246*305e366fSDavid van Moolenbroek } else { 1247*305e366fSDavid van Moolenbroek if ((mslot = get_mslot(pid)) == NO_SLOT) 1248*305e366fSDavid van Moolenbroek return ESRCH; 1249*305e366fSDavid van Moolenbroek 1250*305e366fSDavid van Moolenbroek kslot = NR_TASKS + mslot; 1251*305e366fSDavid van Moolenbroek } 1252*305e366fSDavid van Moolenbroek 1253*305e366fSDavid van Moolenbroek if (oldp == NULL) 1254*305e366fSDavid van Moolenbroek return sizeof(mpd); 1255*305e366fSDavid van Moolenbroek 1256*305e366fSDavid van Moolenbroek kp = &proc_tab[kslot]; 1257*305e366fSDavid van Moolenbroek 1258*305e366fSDavid van Moolenbroek mflags = (pid > 0) ? mproc_tab[mslot].mp_flags : 0; 1259*305e366fSDavid van Moolenbroek 1260*305e366fSDavid van Moolenbroek memset(&mpd, 0, sizeof(mpd)); 1261*305e366fSDavid van Moolenbroek mpd.mpd_endpoint = kp->p_endpoint; 1262*305e366fSDavid van Moolenbroek if (mflags & PRIV_PROC) 1263*305e366fSDavid van Moolenbroek mpd.mpd_flags |= MPDF_SYSTEM; 1264*305e366fSDavid van Moolenbroek if (mflags & (TRACE_ZOMBIE | ZOMBIE)) 1265*305e366fSDavid van Moolenbroek mpd.mpd_flags |= MPDF_ZOMBIE; 1266*305e366fSDavid van Moolenbroek else if ((mflags & TRACE_STOPPED) || RTS_ISSET(kp, RTS_P_STOP)) 1267*305e366fSDavid van Moolenbroek mpd.mpd_flags |= MPDF_STOPPED; 1268*305e366fSDavid van Moolenbroek else if (proc_is_runnable(kp)) 1269*305e366fSDavid van Moolenbroek mpd.mpd_flags |= MPDF_RUNNABLE; 1270*305e366fSDavid van Moolenbroek mpd.mpd_blocked_on = P_BLOCKEDON(kp); 1271*305e366fSDavid van Moolenbroek mpd.mpd_priority = kp->p_priority; 1272*305e366fSDavid van Moolenbroek mpd.mpd_user_time = kp->p_user_time; 1273*305e366fSDavid van Moolenbroek mpd.mpd_sys_time = kp->p_sys_time; 1274*305e366fSDavid van Moolenbroek mpd.mpd_cycles = kp->p_cycles; 1275*305e366fSDavid van Moolenbroek mpd.mpd_kipc_cycles = kp->p_kipc_cycles; 1276*305e366fSDavid van Moolenbroek mpd.mpd_kcall_cycles = kp->p_kcall_cycles; 1277*305e366fSDavid van Moolenbroek if (kslot >= NR_TASKS) { 1278*305e366fSDavid van Moolenbroek mpd.mpd_nice = mproc_tab[mslot].mp_nice; 1279*305e366fSDavid van Moolenbroek strlcpy(mpd.mpd_name, mproc_tab[mslot].mp_name, 1280*305e366fSDavid van Moolenbroek sizeof(mpd.mpd_name)); 1281*305e366fSDavid van Moolenbroek } else 1282*305e366fSDavid van Moolenbroek strlcpy(mpd.mpd_name, kp->p_name, sizeof(mpd.mpd_name)); 1283*305e366fSDavid van Moolenbroek 1284*305e366fSDavid van Moolenbroek return mib_copyout(oldp, 0, &mpd, sizeof(mpd)); 1285*305e366fSDavid van Moolenbroek } 1286