diff options
Diffstat (limited to 'libps')
-rw-r--r-- | libps/ChangeLog | 885 | ||||
-rw-r--r-- | libps/Makefile | 41 | ||||
-rw-r--r-- | libps/common.h | 44 | ||||
-rw-r--r-- | libps/context.c | 165 | ||||
-rw-r--r-- | libps/filters.c | 91 | ||||
-rw-r--r-- | libps/fmt.c | 573 | ||||
-rw-r--r-- | libps/host.c | 112 | ||||
-rw-r--r-- | libps/proclist.c | 692 | ||||
-rw-r--r-- | libps/procstat.c | 1137 | ||||
-rw-r--r-- | libps/ps.h | 1047 | ||||
-rw-r--r-- | libps/pshost.h | 53 | ||||
-rw-r--r-- | libps/spec.c | 1152 | ||||
-rw-r--r-- | libps/tty.c | 155 | ||||
-rw-r--r-- | libps/user.c | 162 | ||||
-rw-r--r-- | libps/write.c | 264 |
15 files changed, 6573 insertions, 0 deletions
diff --git a/libps/ChangeLog b/libps/ChangeLog new file mode 100644 index 00000000..4334ce11 --- /dev/null +++ b/libps/ChangeLog @@ -0,0 +1,885 @@ +2000-01-25 Roland McGrath <roland@baalperazim.frob.com> + + * tty.c (struct ps_tty_abbrev): Add const to member types. + (ps_tty_abbrevs): Make const. + (ps_tty_short_name): Clean up type usage, add consts. + Include null terminator in calculation for short_name allocation size. + Save lengths and use memcpy instead of using strcpy and strcat. + +1999-12-22 Roland McGrath <roland@baalperazim.frob.com> + + * Makefile (HURDLIBS): Add shouldbeinlibc. + +1999-07-10 Roland McGrath <roland@baalperazim.frob.com> + + * common.h: Add #include <sys/mman.h> for munmap decl. + +1999-07-03 Thomas Bushnell, BSG <tb@mit.edu> + + * common.h (VMFREE): Use munmap instead of vm_deallocate. + * procstat.c (merge_procinfo): Likewise. + +1999-06-02 Roland McGrath <roland@baalperazim.frob.com> + + * ps.h (PSTAT_ENV): New macro. + (struct proc_stat): New members `env', `env_len', `env_vm_alloced'. + (proc_stat_env, proc_stat_env_len): New accessor macros. + (PSTAT_USER_BASE): Increase value to leave more room for additions. + * procstat.c (proc_stat_set_flags): Handle environment. + (_proc_stat_free): Likewise. + * spec.c (ps_get_env, ps_env_getter): New function and constant. + (specs): New spec "Env" using ps_env_getter and ps_emit_args. + +1999-05-29 Roland McGrath <roland@baalperazim.frob.com> + + * spec.c (ps_emit_past_time, ps_emit_minutes): Fix return type of + getter fn (int to void). + + * context.c (ps_context_free): Don't call ihash_free on PC->procs + twice! Instead, call it on ttys, ttys_by_cttyid, and users. + + * spec.c (ps_get_start_time, ps_start_time_getter): New function and + constant to report task_basic_info.creation_time time stamp via + "start_time" spec. + (specs): Add "Start" fmt for it. + +1998-10-19 Roland McGrath <roland@baalperazim.frob.com> + + * fmt.c (_fmt_create): Add braces to silence gcc warning. + * procstat.c (summarize_thread_waits): Likewise. + (proc_stat_set_flags): Likewise. + (proc_stat_set_flags): Likewise. + (proc_stat_set_flags): Likewise. + +Sat Jun 7 21:35:37 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_emit_wait): Provide slightly better abbreviations for + various port types. + +Fri May 23 13:13:18 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * procstat.c (summarize_thread_basic_info): Don't include the + kernel's idle threads in the summation. + (summarize_thread_sched_info): Likewise. + (summarize_thread_states): Likewise. + (summarize_thread_waits): Likewise. + +Fri Feb 28 18:11:28 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * fmt.c (ps_fmt_squash): Deal with FIELD->spec being NULL when + advancing over a field. + +Fri Nov 15 19:14:28 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): Allow the user fetch hook to + turn on non-user bits, even if they've already failed in the + standard code. + +Wed Oct 9 14:20:08 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_nominal_string): New function. + (ps_emit_args, ps_emit_string): Use `-' for empty values. + (specs): Use ps_nominal_string for `Args' and `Arg0' fields. + +Tue Oct 8 13:21:55 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): Get rid of INAPP macro. + Fix preconditions of PSTAT_SUSPEND_COUNT. + + * spec.c (ps_emit_seconds, ps_emit_minutes): Interpret + PS_FMT_FIELD_COLON_MOD flag to mean `print zero as "-"'. + + * procstat.c (set_procinfo_flags): Set bits in PS->inapp as appropriate. + (PSTAT_PROCINFO_THREAD, PSTAT_PROCINFO_TASK): New macro. + (PSTAT_PROCINFO_TASK_THREAD_DEP): Renamed from PSTAT_PROCINFO_THREAD. + (PSTAT_PROCINFO): Redefined. + (count_threads): Use PSTAT_PROCINFO_TASK_THREAD_DEP. + (proc_stat_set_flags): Don't use NEED macro for PSTAT_STATE. + +Mon Oct 7 17:40:21 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (struct ps_fmt): Add ERROR field, and rename INVAL to INAPP. + * fmt.c (_fmt_create): Initialize INAPP & ERROR fields. + (ps_fmt_clone): Propagate them. + (ps_fmt_write_proc_stat): Use them. + * procstat.c (proc_stat_set_flags): Add NEED & INAPP macros, and + use them to set the inapp field as well as the failed field. + (_proc_stat_create): Initialize INAPP field. + (proc_stat_thread_create): Initialize FAILED and INAPP fields. + +Sun Oct 6 18:42:52 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (struct ps): Add INAPP field. + + * spec.c (ps_emit_wait): Correct mapping of init ports. + +Mon Sep 30 23:15:42 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (noise_write, noise_len): Correctly calculate the length + of a character printed using an octal escape. + +Thu Sep 12 16:23:47 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * Makefile (HURDLIBS): New variable. + (libps.so): Delete special depedency. + +Fri Aug 2 15:12:19 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (set_procinfo_flags): Pass HAVE to count_threads. + (count_threads): Take new argument HAVE, and use different thread + counting method depending on whether we have thread detail info. + +Thu Jul 18 18:54:07 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * tty.c (ps_tty_abbrevs): Add an entry for /dev/comX -> cX. + +Thu Jul 18 00:45:25 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * Makefile (LCLHDRS): Remove ps_msg.h and ps_term.h. + +Wed Jul 10 22:49:39 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (set_procinfo_flags): Don't use fake "*" wait value + if there's no msgport. + +Mon Jul 8 21:39:58 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (_ps_stream_write_field): If a field is truncatable + (MAX_WIDTH >= 0), take some of our spacing deficit out of it. + +Tue Jul 2 14:43:39 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): Pass PS->task_events_info to + task_info, not its address. + +Thu Jun 27 18:32:27 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * Makefile (LCLHDRS): Add common.h. + +Thu Jun 27 12:33:41 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (struct ps_fmt): Add SRC_LEN field. + (proc_stat_list_clone, ps_fmt_clone): New declarations. + * fmt.c (_fmt_create): Set NEW_FMT->src_len. Use strdup. + (ps_fmt_clone): New function. + * proclist.c (proc_stat_list_clone): New function. + +Mon Jun 3 10:17:43 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_emit_wait): Never truncate what we write. + + * user.c (ps_user_passwd): Check return value of install_passwd + correctly (it's an error_t). + + * ps.h (struct proc_stat): Add PROC_INFO_VM_ALLOCED, + THREAD_WAITS_VM_ALLOCED, and ARGS_VM_ALLOCED fields, + * procstat.c (merge_procinfo) Take a struct proc_stat as an arg, + not all the individual fields. Correctly set OLD_PI_HDR. + Correctly calculate REALLY_NEED. + (set_procinfo_flags): Use new calling merge_procinfo calling convention. + (_proc_stat_free): Use explicit VM_ALLOCED flag for MFREEMEM. + (proc_stat_set_flags): Try mallocing a buffer for PS->args. + Move second call to set_procinfo_flags after msgport suppress test. + Add TEST_MSGPORT_FLAGS variable, & use it. + Be more picky about when we call set_procinfo_flags. + +Sat Jun 1 11:18:58 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (set_procinfo_flags): New function. + (proc_stat_set_flags): Use it, to get msgport validity testing right. + (merge_procinfo): Take and return HAVE instead of using a + reference parameter. Clean up malloced storage if we get an error. + + * spec.c (struct ps_fmt_spec_block): New type. + (specs_add_alias): Use a linked list of ps_fmt_spec_blocks instead of + reallocing a vector of specs. + (ps_fmt_specs_find): Change searching accordingly. + * ps.h (struct ps_fmt_specs): The EXPANSIONS field now points to a + struct ps_fmt_spec_block. Delete EXPANSIONS_ALLOCED field. + + * procstat.c (merge_procinfo): Correctly test for malloc failure. + +Fri May 31 18:36:18 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (MFREEMEM): New macro combining MFREEM & MFREEVM. + (MFREEM, MFREEVM): Macros deleted. + (merge_procinfo): Do mem allocation more efficiently and correctly. + (fetch_procinfo): Do conversion for PI_SIZE from/to units of + sizeof (int), so no one else has to deal with it. + (PROCINFO_MALLOC_SIZE, WAITS_MALLOC_SIZE): New macros. + + * procstat.c (proc_stat_set_flags): After fetching number of + threads to guess whether we need wait info, put it in PS->num_threads. + (merge_procinfo): Avoid vm_allocing a procinfo buffer each time if + we can help it. + Correctly reflect newly fetched info. + (PSTAT_PROCINFO_MERGE, PSTAT_PROCINFO_REFETCH): New macros. + +Wed May 29 11:31:31 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (MP_MGET): Only disable msgport on timeout. + (get_thread_wait): Decrement N in loop. + + * ps.h: Renumber PSTAT_ values to remove dup. + + * procstat.c (merge_procinfo): Don't die if WAITS == 0. + (summarize_thread_waits): Correctly advance NEXT_WAIT. + Skip threads marked `itimer'. + (proc_stat_set_flags): Consider processes with less than 4 threads + to be candidates for a meaningful process wait status. + (get_thread_wait): Use strnlen instead of memchr. + (PSTAT_PROCINFO): Typo: PSTAT_THREAD_WAIT --> PSTAT_THREAD_WAITS. + (PSTAT_USES_MSGPORT): Add PSTAT_THREAD_WAIT. + +Tue May 28 16:36:17 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fmt.c (_fmt_create): Make NAME termination work for posix-mode. + + * fmt.c (ps_fmt_write_proc_stat): Call the output function correctly. + (_fmt_create): Get FIELD's precision, not width, from its spec. + +Fri May 24 13:33:14 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * procstat.c (fetch_procinfo): Always turn on PSTAT_PROC_INFO if + proc_getprocinfo returns successfully. + (merge_procinfo): Update *HAVE with PSTAT_PROC_INFO from + REALLY_HAVE here. + +Wed May 22 19:55:04 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fmt.c (_fmt_create): Increment SRC when reading modifiers. + Recognize '^' modifier. + +Sun May 12 13:33:16 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (PSTAT_PROCINFO, PSTAT_PROCINFO_THREAD): Add + PSTAT_THREAD_WAITS. + (proc_stat_set_flags): Be more careful about when we fetch + thread_wait information, and synthesize a process-summary thread_wait + value for lots of threads. + (summarize_thread_waits): Only give a real summary if there's but + a single user thread. + (fetch_procinfo): Use PSTAT_THREAD_WAITS instead of PSTAT_THREAD_WAIT. + * ps.h (PSTAT_THREAD_WAITS): New macro. + + * procstat.c (merge_procinfo, fetch_procinfo): Change HAVE to be an + input/output parameter. + (proc_stat_set_flags): Change accordingly. + + * procstat.c (get_thread_wait): Correctly advance WAIT. + + * spec.c (specs): Give runtime specs 2 fraction digits by default. + +Thu May 9 17:03:37 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * procstat.c (fetch_procinfo): Pass a reference to PI_FLAGS in + call to fetch_procinfo. + +Mon May 6 16:28:54 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * proclist.c (proc_stat_list_spec_nominal): Take a spec again. + * ps.h (proc_stat_list_spec_nominal): Change FIELD arg to SPEC. + + * user.c (install_passwd): New function. + (ps_user_passwd): Use it. + (ps_user_uname_create, ps_user_passwd_create): New functions. + * ps.h (ps_user_uname_create, ps_user_passwd_create): New declarations. + (ps_fmt_set_output_width): New declaration. + + * spec.c (ps_emit_user_name): New function. + + * ps.h (struct ps_fmt_spec): Remove keep field, add flags field. + (struct ps_fmt_field): Remove at_mod, colon_mod, & keep fields, + add flags field. + (PS_FMT_FIELD_AT_MOD, PS_FMT_FIELD_COLON_MOD, PS_FMT_FIELD_KEEP, + PS_FMT_FIELD_UPCASE_TITLE): New macros. + * spec.c (specs): Initialize flags field, not keep field. + (specs_add_alias): Pass on flags field. + * fmt.c (_fmt_create): Use flags fields, and implement global + flags, and add upcase flag (^). + (ps_fmt_write_titles): Implement PS_FMT_FIELD_UPCASE_TITLE. + * proclist.c (proc_stat_list_spec_nominal): Use flags field, not + keep field. + + * ps.h (struct proc_stat): Remove exec_flags field. + (PSTAT_EXEC_FLAGS): Macro removed. + * procstat.c (add_preconditions, proc_stat_set_flags): Remove + references to exec_flags. + +Sun May 5 00:22:01 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * proclist.c (proc_stat_list_spec_nominal): Take a field, not a spec. + If the field has the keep flag set, don't nominalize it. + * fmt.c (_fmt_create): Change syntax of fields. Add support for + precision and `@', `:', `?', & `!' modifiers. + Add POSIX argument, and support for posix-style format strings. + When using the field name as the title, use the defn, not the user's. + (ps_fmt_squash): Call FN with the field, not the field's spec. + (ps_fmt_squash_flags): Appropiately modify the function we use. + (ps_fmt_create, ps_fmt_creation_error): Add POSIX argument. + * ps.h (struct ps_fmt_spec): Add precision & keep fields. + Change args to OUTPUT_FN. + (struct ps_fmt_field): Add precision, keep, at_mod, & colon_mod fields. + (proc_stat_list_spec_nominal): Change SPEC arg to FIELD. + (ps_fmt_squash): Call FN on the field, not the spec. + (ps_fmt_create, ps_fmt_creation_error): Add POSIX arg. + * spec.c (specs): Add precision & keep fields. + (FG): New macro. + (ps_emit_*): Take FIELD argument instead of WIDTH. + +Thu May 2 00:12:19 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (ps_fmt_creation_error): New declaration. + +Tue Apr 30 18:54:57 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * fmt.c (_fmt_create): New function (was ps_fmt_create). + (ps_fmt_create): Call _fmt_create. + (ps_fmt_creation_error): New function. + + * spec.c (ps_emit_past_time): Implement. + +Mon Apr 29 12:59:08 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_emit_seconds): Use new args to fmt_seconds. + (ps_emit_minutes): Use new args to fmt_minutes. + +Tue Apr 23 13:38:06 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (PSTAT_STATE_P_ATTRS): Fix names of individual flags. + + * spec.c (state_shadows): If a process has no parent don't show + various process attributes (that are likely to be noise). + +Thu Apr 11 18:05:16 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * Makefile (MIGCOMUFLAGS): Delete variable. + + * spec.c (ps_emit_past_time): Return zero. + +Wed Mar 27 15:19:40 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * Makefile (msg-MIGUFLAGS, term-MIGUFLAGS): Add a user prefix of `ps_'. + * procstat.c (proc_stat_set_flags): Use new prefix. + Include "ps_msg.h". + * tty.c (ps_tty_name): Use new prefix. + Include "ps_term.h". + +Mon Mar 25 11:35:48 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ...just about everything...: Get rid of mega typedefs, and just + use structure pointers like other hurd libraries. Other misc cleanups. + + * ps.h (struct ps_fmt_specs): Add EXPANSIONS & EXPANSIONS_ALLOCED. + * spec.c (ps_fmt_specs_find): Use new alias expansion method. + (specv_find, specs_add_alias): New functions. + +Mon Mar 11 16:27:14 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (noise_write): Keep track of amount printed correctly. + +Sat Mar 9 15:52:55 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (iscntl): New macro. + (noise_write, noise_len): Correctly handle MAX < 0 case. + (noise_write): Use new arguments for flush. + (flush): Moved to file scope. Remove END argument and use NEW - 1. + (noise_write): Make T of type unsigned char * so that chars with + the high bit set print correctly. + +Thu Mar 7 19:08:00 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (iscntl): New macro. + (noise_write, noise_len, flush): New functions. + (ps_stream_write, _ps_stream_write_field): Use noise functions. + +Thu Feb 15 00:02:53 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (specs): Right-align the TTY column. + +Wed Feb 14 17:49:17 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (ps_stream_write): Don't screw up on negative spaces. + + * proclist.c (proc_stat_list_add_pids): Add and support new return + parameter PROC_STATS. + (proc_stat_list_add_pid): Add and support new return parameter PS. + (proc_stat_list_add_fn_pids, proc_stat_list_add_id_fn_pids, + proc_stat_list_add_all, proc_stat_list_add_login_coll, + proc_stat_list_add_session, proc_stat_list_add_pgrp): + Add and support new return parameters PROC_STATS & NUM_PROCS. + * ps.h (proc_stat_list_add_pids, proc_stat_list_add_pid, + proc_stat_list_add_all, proc_stat_list_add_login_coll, + proc_stat_list_add_session, proc_stat_list_add_pgrp): + Update declarations. + + * filters.c (ps_alive_filter): New variable. + (ps_alive_p): New function. + * ps.h (ps_alive_filter): New declaration. + +Mon Feb 12 14:34:22 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_emit_wait): For rpcs, put the port first. + +Fri Feb 9 15:55:35 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (struct proc_stat): Add thread_wait, thread_waits, & + thread_waits_len fields. + (PSTAT_THREAD_WAIT): Renamed from PSTAT_THREAD_RPC. + (proc_stat_thread_wait): New macro. + (ps_stream_write_trunc_field): New declaration. + * procstat.c (fetch_procinfo, merge_procinfo): Return wait strings. + (summarize_thread_waits): Return both wait and rpc info. + (get_thread_wait): New function. + (proc_stat_set_flags): Support finding wait info. + Change occurances of PSTAT_THREAD_RPC to PSTAT_THREAD_WAIT. + * spec.c (specs): Change `Rpc' entry to `Wait'. + (ps_emit_wait): New function. + (ps_emit_string, ps_emit_string0): Use ps_stream_write_trunc_field. + (ps_get_wait): Renamed from ps_get_rpc; calling convention changed. + (ps_wait_getter): Renamed from ps_rpc_getter & contents changed accord. + (get_syscall_name, get_rpc_name): New stub functions. + * write.c (ps_stream_write_field): Call _ps_stream_write_field. + (ps_stream_write_trunc_field): New function. + (_ps_stream_write_field): New function, from ps_stream_write_field. + +Sat Feb 3 22:22:01 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (specs, state_shadows, ps_pid_getter, + ps_thread_index_getter, ps_owner_getter, ps_owner_uid_getter, + ps_ppid_getter, ps_pgrp_getter, ps_session_getter, + ps_login_col_getter, ps_num_threads_getter, ps_args_getter, + ps_state_getter, ps_rpc_getter, ps_vsize_getter, ps_rsize_getter, + ps_cur_priority_getter, ps_base_priority_getter, + ps_max_priority_getter, ps_usr_time_getter, ps_sys_time_getter, + ps_tot_time_getter, ps_rmem_frac_getter, ps_cpu_frac_getter, + ps_sleep_getter, ps_susp_count_getter, ps_proc_susp_count_getter, + ps_thread_susp_count_getter, ps_tty_getter, ps_page_faults_getter, + ps_cow_faults_getter, ps_pageins_getter, ps_msgs_sent_getter, + ps_msgs_rcvd_getter, ps_zero_fills_getter): Make const. + * ps.h (ps_getter_t, ps_filter_t, struct ps_filter, + ps_not_leader_filter, ps_ctty_filter, ps_unorphaned_filter, + ps_parent_filter, ps_std_fmt_specs): Make const. + +Mon Jan 15 16:32:31 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (summarize_thread_basic_info): If there are any + running threads, then only average priority from them. + +Sun Jan 14 00:24:55 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (state_shadows): Don't reflect a suspended thread in the + process state display if any thread isn't suspended. + +Sun Dec 24 14:24:52 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * Makefile (installhdrsubdir): New macro (put ps.h in <>, not <hurd/>). + +Sat Dec 23 21:50:58 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * fmt.c (ps_fmt_set_output_width): New function. + +Fri Dec 22 12:21:04 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (struct ps_user_hooks): New structure. + (PSTAT_HOOK, PSTAT_USER_BASE, PSTAT_USER_MASK): New macros. + (struct ps_context): Add USER_HOOKS field. + * procstat.c (_proc_stat_free): Call user cleanup hook. + (proc_stat_set_flags, add_preconditions): Deal with user bits. + * context.c (ps_context_create): Initialize USER_HOOKS field. + +Thu Dec 21 12:04:24 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_get_usr_time, ps_get_sys_time, ps_get_tot_time): + Return a struct timeval instead of mach time_value_t. + (ps_cmp_times): New function. + (ps_emit_seconds, ps_emit_minutes): Use timefmt functions. + (append_fraction, sprint_long_time, ps_emit_nice_seconds): Deleted. + Include <timefmt.h>. + + * ps.h (struct ps_fmt_spec): Add TITLE field. Renamed + DEFAULT_WIDTH field to WIDTH, and move to after TITLE. + (ps_fmt_spec_width): Renamed from ps_fmt_spec_default_width().. + (ps_fmt_spec_title): New macro. + (struct ps_fmt): Add INVAL field. + (ps_fmt_inval): New macro. + * spec.c (specs): Rearrange to use new field layout. + + * fmt.c (ps_fmt_create): Use the new spec fields. + (ps_fmt_write_proc_stat): Support new inval field in FMT. + + * spec.c (ps_fmt_specs_find): Renamed from find_ps_fmt_spec; now + uses a struct ps_fmt_specs instead of an array of specs. + (specs): Renamed from ps_std_fmt_specs; + (ps_std_fmt_specs): Now of type struct ps_fmt_specs, pointing to specs. + * ps.h (ps_fmt_specs_t): New typedef. + (struct ps_fmt_specs): New structure. + (ps_std_fmt_specs): Now of type struct ps_fmt_specs. + (ps_fmt_specs_find): Renamed from find_ps_fmt_spec; now uses a + struct ps_fmt_specs instead of an array of specs. + (ps_fmt_create): Now takes a ps_fmt_specs_t structure instead of + an array of specs. + + * fmt.c (ps_fmt_create): Now takes a ps_fmt_specs_t instead of an + array of specs. Also fixup call to ps_fmt_specs_find(). + + * ps.h (struct proc_stat): Add failed and hook fields. + * procstat.c (proc_stat_set_flags): Support the failed field. + (_proc_stat_create): Initialize the failed and hook fields. + +Wed Dec 20 12:49:24 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_emit_nz_int): Write `-' when the value is 0, rather + than mangling the output. + +Sun Dec 17 03:09:31 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): If there's no owner, set the + uid to -1 (and the owner to null), instead of failing. + * spec.c (ps_emit_uid): Use an int uid, and emit "-" for none. + (ps_emit_uname, ps_cmp_uids, ps_cmp_unames, ps_nominal_user): + Handle NULL users. + + * filters.c (ps_not_leader_p): Renamed from ps_not_sess_leader_p. + (ps_not_leader_filter): Renamed from ps_not_sess_leader_filter. + (ps_unorphaned_p): Include login leaders as well as session leaders. + * ps.h (ps_not_leader_filter): Renamed from ps_not_sess_leader_filter. + +Sat Dec 16 23:42:27 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (proc_stat_owner_uid): New macro. + (PSTAT_OWNER_UID): New macro. + (struct proc_stat): Add owner_uid field. + * procstat.c (proc_stat_set_flags): Add support for PROC_OWNER_UID. + (add_preconditions): Add preconditions for owner_uid (& owner). + * spec.c (ps_owner_uid_getter): New variable. + (ps_get_owner_uid, ps_nominal_uid): New functions. + (ps_std_fmt_specs): Make "UID" use owner_uid rather than owner. + (own_uid): New variable (was function local). + * filters.c (ps_own_filter): Depend on PSTAT_OWNER_UID. + (ps_own_p): Account for there being no uid. + +Thu Nov 16 12:51:34 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * write.c (ps_stream_write_field): Trim spaces from BUF. + +Wed Nov 15 18:55:26 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * ps.h (ps_fmt_write_titles, ps_fmt_write_proc_stat, + proc_stat_list_fmt, ps_emit_int, ps_emit_nz_int, ps_emit_priority, + ps_emit_percent, ps_emit_num_blocks, ps_emit_nice_int, + ps_emit_nice_seconds, ps_emit_seconds, ps_emit_uid, ps_emit_uname, + ps_emit_string0, ps_emit_string, ps_emit_tty_name, ps_emit_state, + ps_stream_write, ps_stream_space, ps_stream_pad, + ps_stream_newline, ps_stream_write_field, ps_stream_write_int_field): + Use new STREAM parameter instead of old one and count. + (ps_stream_write): Renamed from ps_write_string. + (ps_stream_space): Renamed from ps_write_spaces. + (ps_stream_pad): Renamed from ps_write_padding. + (ps_stream_write_field): Renamed from ps_write_field. + (ps_stream_write_int_field): Renamed from ps_write_int_field. + (ps_stream_newline): New declaration. + + * fmt.c (ps_fmt_write_titles, ps_fmt_write_proc_stat): Use new + write function names. Use new STREAM parameter instead of old one + and count. + * proclist.c (proc_stat_list_fmt): Ditto. + * spec.c (ps_emit_int, ps_emit_nz_int, ps_emit_priority, + ps_emit_percent, ps_emit_num_blocks, ps_emit_nice_int, + ps_emit_nice_seconds, ps_emit_seconds, ps_emit_uid, ps_emit_uname, + ps_emit_string0, ps_emit_string, ps_emit_tty_name, ps_emit_state): + Ditto. + (ps_emit_seconds): Remove leading spaces from what we print. + + * write.c (ps_stream_write): Renamed from ps_write_string. + (ps_stream_space): Renamed from ps_write_spaces. + (ps_stream_pad): Renamed from ps_write_padding. + (ps_stream_write_field): Renamed from ps_write_field. + (ps_stream_write_int_field): Renamed from ps_write_int_field. + (ps_stream_write, ps_stream_space, ps_stream_pad, + ps_stream_newline, ps_stream_write_field, ps_stream_write_int_field): + Use new STREAM parameter instead of old one and count. + Use new function names. + (ps_stream_write, ps_stream_space): Support negative spaces. + (ps_stream_newline): New function. + (ps_stream_pad, ps_stream_write_field): Use negative spaces. + +Tue Nov 7 17:43:48 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_base_priority_getter, ps_cur_priority_getter, + ps_get_base_priority, ps_get_cur_priority): Get this info using + PSTAT_THREAD_BASIC instead of PSTAT_THREAD_SCHED. + * procstat.c (summarize_thread_basic_info): Summarize available + priority info too. + (summarize_thread_sched_info): Do max_ & depress_priority too. + + * procstat.c (proc_stat_set_flags): Initialize the proc_info and + proc_info_size fields if they've never been set before. + Always update proc_getprocinfo fields, even if we already had them. + (add_preconditions): Correct preconditions for PSTAT_STATE. + (PSTAT_TEST_MSGPORT): Renamed from SHOULD_SUPPRESS_MSGPORT_FLAGS. + (PSTAT_USES_MSGPORT): New macro. + (SUPPRESS_MSGPORT_FLAGS): Use PSTAT_USES_MSGPORT, not PSTAT_MSGPORT. + (proc_stat_set_flags): Use PSTAT_TEST_MSGPORT. + (merge_procinfo): Only copy old task info if we actually had it. + (proc_stat_set_flags): Don't unnecessarily grab procinfo stuff. + +Tue Oct 31 14:03:53 1995 Miles Bader <miles@gnu.ai.mit.edu> + + * spec.c (ps_rpc_getter): New variable. + (ps_get_rpc): New function. + (ps_std_fmt_specs): Add "RPC" entry. + (ps_emit_nz_int): New function. + + * ps.h (PSTAT_STATE_P_LOGINLDR, PSTAT_STATE_P_WAIT, + PSTAT_STATE_P_GETMSG): New flags. + * procstat.c (add_preconditions): Don't require PSTAT_EXEC_FLAGS for + getting the state anymore (but do require PSTAT_{TASK,THREAD}_BASIC). + (proc_stat_state_tags): Update to reflect new flags. + (proc_stat_set_flags): Set new flags. + + * procstat.c (PSTAT_PROCINFO, PSTAT_PROCINFO_THREAD): New macros. + (fetch_procinfo, merge_procinfo): New functions. + (SHOULD_SUPPRESS_MSGPORT_FLAGS): Change to use more accurate flags. + (should_suppress_msgport): Use new fields. + (summarize_thread_basic_info, summarize_thread_sched_info, + summarize_thread_states, summarize_thread_rpcs, count_threads): + New functions. + (proc_stat_set_flags): Support the new division of PSTAT_INFO into + individual flags, and support getting thread information by + using the thread's origin proc_stat. + (_proc_stat_free): Free the thread_basic_info and + thread_sched_info fields if necessary. + (proc_stat_thread_create): Don't require that the source process + have thread information around; it will be fetched later if necessary. + + * spec.c (ps_ppid_getter, ps_pgrp_getter, ps_session_getter, + ps_login_col_getter): Use PSTAT_PROC_INFO, not PSTAT_INFO. + (ps_get_ppid, ps_get_pgrp, ps_get_session, ps_get_login_col): Use + proc_stat_proc_info, not proc_stat_info. + (ps_vsize_getter, ps_rsize_getter, ps_rmem_frac_getter, + ps_proc_susp_count_getter): Use PSTAT_TASK_BASIC, not PSTAT_INFO. + (ps_get_vsize, ps_get_rsize, ps_get_rmem_frac, ps_get_proc_susp_count): + Use proc_stat_task_basic_info, not proc_stat_info. + (ps_cur_priority_getter, ps_base_priority_getter, + ps_max_priority_getter): Use PSTAT_THREAD_SCHED, not PSTAT_THREAD_INFO. + (ps_usr_time_getter, ps_sys_time_getter, ps_tot_time_getter, + ps_cpu_frac_getter, ps_sleep_getter): + Use PSTAT_THREAD_BASIC, not PSTAT_THREAD_INFO. + + * filters.c (ps_own_filter): Use PSTAT_PROC_INFO, not PSTAT_INFO. + (ps_own_p): Use proc_stat_proc_info, not proc_stat_info. + + * ps.h (proc_stat_num_threads): Use the num_threads field. + (proc_stat_thread_sched_info, proc_stat_thread_basic_info): Don't + take the address, now that the fields used are pointers themselves. + (proc_stat_thread_rpc, proc_stat_task_basic_info): New macros. + (proc_stat_proc_info): Renamed from proc_stat_info. + (PSTAT_PROC_INFO): Renamed from PSTAT_INFO. + (PSTAT_TASK_BASIC, PSTAT_THREAD_BASIC, PSTAT_THREAD_SCHED, + PSTAT_THREAD_RPC): New macros. + (struct proc_stat): info & info_len --> proc_info & proc_info_len. + Add the num_threads, task_basic_info, and thread_rpc fields. + thread_basic_info & thread_sched_info are now pointers. + +Mon Oct 9 14:57:48 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu> + + * Makefile: Specify shared library dependencies. + +Fri Aug 25 18:55:51 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * ps.h (ps_std_fmt_specs): Declare extern. + +Wed Aug 23 15:04:51 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * Makefile (OBJS): Just put the migs stubs directly in here. + (REMHDRS, MIGSTUBS): Removed. + +Sat Aug 19 11:49:06 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): Actually set the P_STOP bit. + +Fri Aug 18 16:43:41 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * ps.h (PSTAT_STATE_*): All proc state flags reorganized and + renamed to PSTAT_STATE_P_* for process-global bits, and + PSTAT_STATE_T_* for per-thread bits. + * procstat.c (proc_stat_state_tags): Reordered to reflect the new + ordering of the state bits. + (thread_state, proc_stat_set_flags): Use the new state bits. + * spec.c (ps_emit_state): Rearrange things to reflect the new + state bits. + (state_shadows): New variable. + (ps_emit_state): Use the state_shadows list to turn off some states. + * filters.c (ps_not_sess_leader_p, ps_unorphaned_p, ps_parent_p): + Use the new state bits. + +Sat Jul 8 13:34:20 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * tty.c (ps_tty_short_name): That assignment around which extra + parents were put was actually supposed to be a test! Make it so... + +Thu Jul 6 22:25:20 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu> + + * tty.c (ps_tty_short_name): Put extra parens around assignment + inside if test. + +Thu Jul 6 15:36:04 1995 Miles Bader <miles@geech.gnu.ai.mit.edu> + + * Makefile: Remove include dependencies. + +Thu Jun 29 15:29:05 1995 Miles Bader <miles@geech.gnu.ai.mit.edu> + + * Makefile (REMHDRS): New variable. + ($(OBJS)): depend on ../libihash/ihash.h. + * ps.h: Include hurd/ihash.h instead of ihash.h. + +Wed May 31 13:09:04 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * fmt.c (ps_fmt_create): Don't mutate the format spec name in the + fmt_spec list we're passed just to get correctly capitalized + titles. Instead, do things correctly by making enough room to + store our own version of the title string, which we can do with + what we please. + + * ps.h (ps_own_filter, ps_not_sess_leader_filter, ps_ctty_filter, + ps_unorphaned_filter, ps_parent_filter): Declare these as extern + so that the linker will bring in the initialized version (it's not + doing so otherwise may be a bug). + +Thu May 4 20:01:32 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): If a msg port call times out, + disable use of that msg port. + * Makefile (MIGSTUBS, term-MIGUFLAGS, msg-MIGUFLAGS): Compile our + own msg & term user stubs to add msg timeouts. + +Wed May 3 11:32:52 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * proclist.c (proc_stat_list_for_each): New function for iterating + over proc_stat_lists. + (proc_stat_list_spec_nominal): New function for deciding if a + particular spec is always nominal. + * ps.h: Add entries for proc_stat_list_for_each and + proc_stat_list_spec_nominal. + + * fmt.c (ps_fmt_squash, ps_fmt_squash_flags): Recalculate the set + of ps flags needed by the fmt in ps_fmt_squash. + +Tue May 2 12:25:57 1995 Miles Bader <miles@geech.gnu.ai.mit.edu> + + * ps.h (ps_fmt_squash): Rename to ps_fmt_squash_flags. + * fmt.c (ps_fmt_squash, ps_fmt_squash_flags): Rename ps_fmt_squash to + ps_fmt_squash_flags, moving most of the guts into a new more + general ps_fmt_squash, which is usable for other things than flags. + + * ps.h (struct ps_fmt_spec): Add the nominal_fn field, which will + be used to decide whether values are `unexciting'. + * spec.c (ps_std_fmt_specs): Add values for the nominal_fn field. + (ps_nominal_zint, ps_nominal_user, ps_nominal_pri, ps_nominal_nth): + Possible nominal funs. + + * ps.h (struct proc_stat): Add the suspend_count field, along with + PSTAT_SUSPEND_COUNT, and proc_stat_suspend_count(ps). + * procstat.c (proc_stat_set_flags, add_preconditions): Add support for + the suspend_count field. + * spec.c (ps_std_fmt_specs): Add the Susp (task/thread suspend count), + PSusp (task suspend count), and TSusp (thread suspend count) output + specs. + + * procstat.c (add_preconditions): A new function that calculates inter- + flag dependencies; code moved here from from proc_stat_set_flags. + (should_suppress_msgport): A new function that returns true when + there's some condition indicating that we shouldn't use a process's msg + port. + (proc_stat_set_flags): Avoid using a process's msg port when it may be + unusable. + + * ps.h (PSTAT_STATE_FORKED): A new flag to replace PSTAT_STATE_EXECED; + we want to the flags to mark exceptional conditions, and this is rarer. + * procstat.c (proc_stat_set_flags): Set PSTAT_STATE_FORKED instead of + PSTAT_STATE_EXECED. + (proc_stat_state_tags): Change the user state letter to "f" from "e". + +Sun Apr 23 15:38:39 1995 Miles Bader <miles@geech.gnu.ai.mit.edu> + + * Makefile: Set libname. + +Fri Apr 7 11:12:15 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * proclist.c (proc_stat_list_sort1): Always keep threads after their + process when sorting! + + * spec.c (ps_emit_state): If a process is marked as stopped, then + don't mention sleeping or idle threads' status (as that's presumably + the signal thread). + + * ps.h: Add decls for proc_stat_list_add_pgrp & ps_tty_short_name. + + * proclist.c (proc_stat_list_add_all, proc_stat_list_add_login_coll, + proc_stat_list_add_session): Move most of the functionality into + proc_stat_list_add_[id_]fn_pids. + (proc_stat_list_add_pgrp): New function, adds pids for a pgrp. + + * tty.c (ps_tty_short_name): New function; functionality used to be in + spec.c + (ps_tty_create, ps_tty_free): Add short_name fields. + * spec.c (ps_emit_tty_name): Move guts into into ps_tty_short_name. + + * Just about everything: tighten up types used (i.e., don't use int + for everything). + +Wed Apr 5 22:42:24 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * spec.c (ps_std_fmt_specs): Add the `Arg0' spec, which is the + same as `Args', but only prints the first one. Change MsgsIn + and MsgsOut to MsgIn and MsgOut. + +Tue Apr 4 20:13:55 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * ps.h: Add the PSTAT_NO_MSGPORT flag, which when set disables any + use of the process's message port. + * procstat.c (proc_stat_set_flags): If PSTAT_NO_MSGPORT is set, + don't use the msg port. + +Wed Mar 29 15:36:43 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> + + * procstat.c (proc_stat_set_flags): Don't barf if a process has + zero threads. + +Tue Mar 28 10:33:08 1995 Miles Bader <miles@duality.gnu.ai.mit.edu> + + * ps.h: Add the exec_flags field to the proc_stat structure, and + add PSTAT_STATE_TRACED. + * procstat.c (proc_stat_set_flags): Add support for the exec_flags + field, and make the state bits calculation use that to support the + PSTAT_STATE_TRACED bit. + +Mon Mar 20 20:51:51 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu> + + * user.c (ps_user_passwd) [COUNT, COPY]: Use `if' statement. + * spec.c (ps_emit_num_blocks): Use int format for int arg. + (sprint_frac_value): Likewise. + * write.c (ps_write_int_field): Likewise. + * host.c (ps_host_basic_info): Cast third arg to host_info. + (ps_host_sched_info): Likewise. + (ps_host_load_info): Likewise. + + * filters.c: Include <unistd.h>. + * context.c: Include <hurd/term.h>. + * tty.c: Likewise. + * spec.c: Include <string.h>. + * procstat.c: Likewise. + + * host.c: Don't include "pshost.h". + * spec.c: Likewise. + + * ps.h: Include <errno.h>. + (ps_get_host, ps_host_basic_info, ps_host_sched_info, + ps_host_load_info): Copied here from pshost.h. + (ps_write_string, ps_write_spaces, ps_write_padding, + ps_write_field, ps_write_int_field): Copied here from pswrite.h + * pshost.h, pswrite.h: Delete files. + + * New ChangeLog, moved into canonical directory structure. + Old ChangeLog is in .../hurd/utils/ps.ChangeLog. diff --git a/libps/Makefile b/libps/Makefile new file mode 100644 index 00000000..20e0dc35 --- /dev/null +++ b/libps/Makefile @@ -0,0 +1,41 @@ +# Makefile for libps +# +# Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc. +# Written by Michael I. Bushnell. +# +# This file is part of the GNU Hurd. +# +# The GNU Hurd is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# The GNU Hurd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := libps +makemode := library + +libname = libps +SRCS = context.c filters.c fmt.c host.c proclist.c procstat.c spec.c \ + tty.c user.c write.c +LCLHDRS=ps.h common.h +installhdrs=$(LCLHDRS) +installhdrsubdir = . + +HURDLIBS=ihash shouldbeinlibc +OBJS = $(SRCS:.c=.o) msgUser.o termUser.o + +msg-MIGUFLAGS = -D'MSG_IMPORTS=waittime 1000;' -DUSERPREFIX=ps_ +term-MIGUFLAGS = -D'TERM_IMPORTS=waittime 1000;' -DUSERPREFIX=ps_ + +ps_%.h: %_U.h + sed 's/_$*_user_/_ps_$*_user_/g' $< > $@ + +include ../Makeconf diff --git a/libps/common.h b/libps/common.h new file mode 100644 index 00000000..6c44641e --- /dev/null +++ b/libps/common.h @@ -0,0 +1,44 @@ +/* Handy common functions for things in libps. + + Copyright (C) 1995, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <sys/mman.h> + +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define MAX(x, y) ((x) < (y) ? (y) : (x)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +/* Allocate memory to store an element of type TYPE */ +#define NEW(type) ((type *)malloc(sizeof(type))) +/* Allocate a vector of type TYPE *, consisting of LEN elements of type TYPE */ + +#define NEWVEC(type,len) ((type *)malloc(sizeof(type)*(len))) +/* Change the size of the vector OLD, of type TYPE *, to be LEN elements of type TYPE */ +#define GROWVEC(old,type,len) \ + ((type *)realloc((void *)(old),(unsigned)(sizeof(type)*(len)))) + +#define FREE(x) (void)free((void *)x) +#define VMFREE(x, len) munmap((caddr_t)x, len) + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif diff --git a/libps/context.c b/libps/context.c new file mode 100644 index 00000000..eddbc50c --- /dev/null +++ b/libps/context.c @@ -0,0 +1,165 @@ +/* The ps_context type, for per-procserver and somewhat global state. + + Copyright (C) 1995,96,99 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <hurd/term.h> + +#include "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* Returns in PC a new ps_context for the proc server SERVER. If a memory + allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t +ps_context_create (process_t server, struct ps_context **pc) +{ + error_t err_procs, err_ttys, err_ttys_by_cttyid, err_users; + + *pc = NEW (struct ps_context); + if (*pc == NULL) + return ENOMEM; + + (*pc)->server = server; + (*pc)->user_hooks = 0; + err_procs = ihash_create (&(*pc)->procs); + err_ttys = ihash_create (&(*pc)->ttys); + err_ttys_by_cttyid = ihash_create (&(*pc)->ttys_by_cttyid); + err_users = ihash_create (&(*pc)->users); + + if (err_procs || err_ttys || err_ttys_by_cttyid) + /* Some allocation error occurred, backout any successful ones and fail. */ + { + if (!err_procs) ihash_free ((*pc)->procs); + if (!err_users) ihash_free ((*pc)->users); + if (!err_ttys) ihash_free ((*pc)->ttys); + if (!err_ttys_by_cttyid) ihash_free ((*pc)->ttys_by_cttyid); + free (*pc); + return ENOMEM; + } + + ihash_set_cleanup ((*pc)->procs, + (void (*)(void *, void *arg))_proc_stat_free, + NULL); + ihash_set_cleanup ((*pc)->ttys, + (void (*)(void *, void *arg))ps_tty_free, + NULL); + ihash_set_cleanup ((*pc)->users, + (void (*)(void *, void *arg))ps_user_free, + NULL); + + return 0; +} + +/* Frees PC and any resources it consumes. */ +void +ps_context_free (struct ps_context *pc) +{ + ihash_free (pc->procs); + ihash_free (pc->ttys); + ihash_free (pc->ttys_by_cttyid); + ihash_free (pc->users); + free (pc); +} + +/* ---------------------------------------------------------------- */ + +/* Return the value in HT indexed by the key ID. If it doesn't exist create + it by calling CREATE with ID and a return location pointer as arguments + (CREATE should return either an error-code or 0 if no error occurs), and + cache it in HT. */ +static error_t +lookup (int id, ihash_t ht, error_t (*create)(int id, void **), void **value) +{ + *value = ihash_find (ht, id); + if (*value == NULL) + { + error_t err = create (id, value); + if (err) + return err; + ihash_add (ht, id, *value, NULL); + } + return 0; +} + +/* Find a proc_stat for the process referred to by PID, and return it in + PS. If an error occurs, it is returned, otherwise 0. */ +error_t +ps_context_find_proc_stat (struct ps_context *pc, pid_t pid, struct proc_stat **ps) +{ + error_t create (int pid, void **value) + { + return _proc_stat_create (pid, pc, (struct proc_stat **)value); + } + return lookup (pid, pc->procs, create, (void **)ps); +} + +/* Find a ps_tty for the terminal referred to by the port TTY_PORT, and + return it in TTY. If an error occurs, it is returned, otherwise 0. */ +error_t +ps_context_find_tty (struct ps_context *pc, mach_port_t tty_port, + struct ps_tty **tty) +{ + return lookup (tty_port, + pc->ttys, + (error_t (*)(int id, void **result))ps_tty_create, + (void **)tty); +} + +/* Find a ps_tty for the terminal referred to by the ctty id port + CTTYID_PORT, and return it in TTY. If an error occurs, it is returned, + otherwise 0. */ +error_t +ps_context_find_tty_by_cttyid (struct ps_context *pc, mach_port_t cttyid_port, + struct ps_tty **tty) +{ + error_t create (int cttyid_port, void **value) + { + if (cttyid_port == MACH_PORT_NULL) + { + *value = 0; + return 0; + } + else + { + int tty_port; + error_t err = termctty_open_terminal (cttyid_port, 0, &tty_port); + if (err) + return err; + else + return ps_context_find_tty (pc, tty_port, (struct ps_tty **)value); + } + } + + return lookup (cttyid_port, pc->ttys, create, (void **)tty); +} + +/* Find a ps_user for the user referred to by UID, and return it in U. */ +error_t +ps_context_find_user (struct ps_context *pc, uid_t uid, struct ps_user **u) +{ + return lookup (uid, + pc->users, + (error_t (*)(int id, void **result))ps_user_create, + (void **)u); +} diff --git a/libps/filters.c b/libps/filters.c new file mode 100644 index 00000000..4fac0390 --- /dev/null +++ b/libps/filters.c @@ -0,0 +1,91 @@ +/* Some ps_filters to restrict proc_stat_lists in various ways. + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <pwd.h> +#include <unistd.h> + +#include "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +static int +ps_own_p (struct proc_stat *ps) +{ + static int own_uid = -2; /* -1 means no uid at all. */ + if (own_uid == -2) + own_uid = getuid (); + return own_uid >= 0 && own_uid == proc_stat_owner_uid (ps); +} +struct ps_filter ps_own_filter = +{"own", PSTAT_OWNER_UID, ps_own_p}; + +static int +ps_not_leader_p (struct proc_stat *ps) +{ + return + !(proc_stat_state (ps) & (PSTAT_STATE_P_SESSLDR | PSTAT_STATE_P_LOGINLDR)); +} +struct ps_filter ps_not_leader_filter = +{"not-sess-leader", PSTAT_STATE, ps_not_leader_p}; + +static int +ps_unorphaned_p (struct proc_stat *ps) +{ + int state = proc_stat_state (ps); + return + !(state & PSTAT_STATE_P_ORPHAN) + || (state & (PSTAT_STATE_P_SESSLDR | PSTAT_STATE_P_LOGINLDR)); +} +struct ps_filter ps_unorphaned_filter = +{"unorphaned", PSTAT_STATE, ps_unorphaned_p}; + +static int +ps_ctty_p (struct proc_stat *ps) +{ + return proc_stat_cttyid (ps) != MACH_PORT_NULL; +} +struct ps_filter ps_ctty_filter = +{"ctty", PSTAT_CTTYID, ps_ctty_p}; + +static int +ps_parent_p (struct proc_stat *ps) +{ + return !(proc_stat_state (ps) & PSTAT_STATE_P_NOPARENT); +} +struct ps_filter ps_parent_filter = +{"parent", PSTAT_STATE, ps_parent_p}; + +static int +ps_alive_p (struct proc_stat *ps) +{ + ps_flags_t test_flag = + proc_stat_is_thread (ps) ? PSTAT_THREAD_BASIC : PSTAT_PROC_INFO; + if (proc_stat_has (ps, test_flag)) + return 1; + proc_stat_set_flags (ps, test_flag); + return proc_stat_has (ps, test_flag); +} +struct ps_filter ps_alive_filter = +{"alive", 0, ps_alive_p}; diff --git a/libps/fmt.c b/libps/fmt.c new file mode 100644 index 00000000..eae08ffe --- /dev/null +++ b/libps/fmt.c @@ -0,0 +1,573 @@ +/* Implements the ps_fmt type, which describes how to output a user-readable + version of a proc_stat. + + Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> + +#include "ps.h" +#include "common.h" + +/* An internal version of ps_fmt_create that takes various extra args. If + POSIX is true, parse a posix-std format string. If ERR_STRING is non-0 + and EINVAL is returned, then a malloced string will be returned in + ERR_STRING describing why. */ +static error_t +_fmt_create (char *src, int posix, struct ps_fmt_specs *fmt_specs, + struct ps_fmt **fmt, char **err_string) +{ + struct ps_fmt *new_fmt; + int needs = 0; + int fields_alloced = 10; + /* Initial values for CLR_FLAGS & INV_FLAGS, so the user may specify + string-wide defaults. */ + int global_clr_flags = 0, global_inv_flags = 0; + struct ps_fmt_field *fields = NEWVEC (struct ps_fmt_field, fields_alloced); + struct ps_fmt_field *field = fields; /* current last field */ + + if (fields == NULL) + return ENOMEM; + + new_fmt = NEW (struct ps_fmt); + if (fmt == NULL) + { + FREE (fields); + return ENOMEM; + } + + /* Make a private copy of SRC so we can mutate it. */ + new_fmt->src_len = strlen (src) + 1; + new_fmt->src = strdup (src); + if (new_fmt->src == NULL) + { + FREE (fields); + FREE (new_fmt); + return ENOMEM; + } + + src = new_fmt->src; + while (*src != '\0') + { + if (field - fields == fields_alloced) + /* Time to grow FIELDS to make room for more. */ + { + int offs = field - fields; + + fields_alloced += 10; + fields = GROWVEC (fields, struct ps_fmt_field, fields_alloced); + + if (fields == NULL) + { + FREE (new_fmt); + FREE (new_fmt->src); + return ENOMEM; + } + + field = fields + offs; + } + + if (posix) + /* Posix fields are always adjacent to one another. */ + { + field->pfx = " "; + field->pfx_len = 1; + } + else + /* Find the text to be reproduced verbatim between the last field and + the next one; we'll add this a prefix to FIELD. */ + { + field->pfx = src; + while (*src != '\0' && *src != '%') + src++; + field->pfx_len = src - field->pfx; + } + + field->spec = NULL; + field->title = NULL; + field->width = 0; + + if (*src != '\0') + /* Another format-spec. */ + { + char *name; + int sign = 1; + int explicit_width = 0, explicit_precision = 0; + int quoted_name = 0; /* True if the name is quoted with { ... }. */ + /* Modifications to the spec's flags -- the bits in CLR_FLAGS are + cleared from it, and then the bits in INV_FLAGS are inverted. */ + int clr_flags = global_clr_flags, inv_flags = global_inv_flags; + + if (! posix) + src++; /* skip the '%' */ + + /* Set modifiers. */ + while (*src == '@' || *src == ':' + || *src == '!' || *src == '?' || *src == '^') + { + if (*src == '@') + inv_flags ^= PS_FMT_FIELD_AT_MOD; /* Toggle */ + else if (*src == ':') + inv_flags ^= PS_FMT_FIELD_COLON_MOD; /* Toggle */ + else if (*src == '^') + inv_flags ^= PS_FMT_FIELD_UPCASE_TITLE; /* Toggle */ + else if (*src == '!') + { /* Set */ + clr_flags |= PS_FMT_FIELD_KEEP; + inv_flags |= PS_FMT_FIELD_KEEP; + } + else if (*src == '?') + { /* Clear */ + clr_flags |= PS_FMT_FIELD_KEEP; + inv_flags &= ~PS_FMT_FIELD_KEEP; + } + src++; + } + + /* Read an explicit field width. */ + field->width = 0; + if (*src == '-') + sign = -1, src++; + while (isdigit (*src)) + { + field->width = field->width * 10 + (*src++ - '0'); + explicit_width = TRUE; + } + + /* Read an explicit field precision. */ + field->precision = 0; + if (*src == '.') + while (isdigit (*++src)) + { + field->precision = field->precision * 10 + (*src - '0'); + explicit_precision = 1; + } + + /* Skip `{' between optional width and spec name. */ + if (*src == '{') + { + src++; + quoted_name = 1; + } + else if (!isalnum (*src) && *src != '_') + /* This field spec doesn't have a name, so use its flags fields + to set the global ones, and skip it. */ + { + global_clr_flags = clr_flags; + global_inv_flags = inv_flags; + continue; + } + + name = src; + + if (posix) + /* Posix-style field spec: `NAME' or `NAME=TITLE'. Only commas + can separate fields. */ + { + int stop = quoted_name ? '}' : ','; + while (*src != '\0' && *src != stop && *src != '=') + src++; + if (*src == '=') + /* An explicit title. */ + { + *src++ = '\0'; /* NUL-terminate NAME. */ + field->title = src; + while (*src != '\0' && *src != stop) + src++; + } + *src++ = '\0'; /* NUL terminhate NAME. */ + } + else + /* A gnu-style field spec: `NAME' or `NAME:TITLE'. */ + { + while (quoted_name + ? (*src != '\0' && *src != '}' && *src != ':') + : (isalnum (*src) || *src == '_')) + src++; + if (quoted_name && *src == ':') + /* An explicit title. */ + { + *src++ = '\0'; /* NUL-terminate SRC. */ + field->title = src; + while (*src != '\0' && *src != '}') + src++; + } + + /* Move the name down one byte (we know there's room, at least + the leading `%') so that we have room to NUL-terminate the + name for which we're searching. We also adjust any pointers + into this spec-string accordingly. */ + bcopy (name, name - 1, src - name); + name--; + if (field->title) + field->title--; + + /* Now that we've made room, do the termination of NAME. */ + src[-1] = '\0'; + } + + field->spec = ps_fmt_specs_find (fmt_specs, name); + if (! field->spec) + /* Failed to find any named spec called NAME. */ + { + if (err_string) + asprintf (err_string, "%s: Unknown format spec", name); + + FREE (new_fmt->src); + FREE (fields); + FREE (new_fmt); + + return EINVAL; + } + + if (! field->title) + { + /* No explicit title specified in the fmt string. */ + if (field->spec->title) + field->title = field->spec->title; /* But the spec has one. */ + else + field->title = field->spec->name; /* Just use field name. */ + } + + /* Add FIELD's required pstat_flags to FMT's set */ + needs |= ps_getter_needs (ps_fmt_spec_getter (field->spec)); + + if (! explicit_width) + field->width = field->spec->width; + if (! explicit_precision) + field->precision = field->spec->precision; + + field->flags = (field->spec->flags & ~clr_flags) ^ inv_flags; + + if (quoted_name && *src == '}') + /* Skip optional trailing `}' after the spec name. */ + src++; + if (posix) + /* Skip interfield noise. */ + { + if (*src == ',') + src++; + while (isspace (*src)) + src++; + } + + /* Remember the width's sign (we put it here after possibly using a + default width so that the user may include a `-' with no width + to flip the justification of the default width). */ + field->width *= sign; + + { + /* Force the field to be wide enough to hold the title. */ + int width = field->width; + int tlen = strlen (field->title); + if (width != 0 && tlen > ABS (width)) + field->width = (width > 0 ? tlen : -tlen); + } + } + + field++; + } + + new_fmt->fields = fields; + new_fmt->num_fields = field - fields; + new_fmt->needs = needs; + new_fmt->inapp = posix ? "-" : 0; + new_fmt->error = "?"; + + *fmt = new_fmt; + + return 0; +} + +/* Make a PS_FMT_T by parsing the string SRC, searching for any named + field specs in FMT_SPECS, and returning the result in FMT. If a memory + allocation error occurs, ENOMEM is returned. If SRC contains an unknown + field name, EINVAL is returned. Otherwise 0 is returned. If POSIX is + true, a posix-style format string is parsed, otherwise see ps.h for an + explanation of how FMT is derived from SRC. */ +error_t +ps_fmt_create (char *src, int posix, struct ps_fmt_specs *fmt_specs, + struct ps_fmt **fmt) +{ + return _fmt_create (src, posix, fmt_specs, fmt, 0); +} + +/* Given the same arguments as a previous call to ps_fmt_create that returned + an error, this function returns a malloced string describing the error. */ +void +ps_fmt_creation_error (char *src, int posix, struct ps_fmt_specs *fmt_specs, + char **error) +{ + struct ps_fmt *fmt; + error_t err = _fmt_create (src, posix, fmt_specs, &fmt, error); + if (err != EINVAL) /* ? */ + asprintf (error, "%s", strerror (err)); + if (! err) + ps_fmt_free (fmt); +} + +/* Free FMT, and any resources it consumes. */ +void +ps_fmt_free (struct ps_fmt *fmt) +{ + FREE (fmt->src); + FREE (fmt->fields); + FREE (fmt); +} + +/* Return a copy of FMT in COPY, or an error. This is useful if, for + instance, you would like squash a format without destroying the original. */ +error_t +ps_fmt_clone (struct ps_fmt *fmt, struct ps_fmt **copy) +{ + struct ps_fmt *new = NEW (struct ps_fmt); + struct ps_fmt_field *fields = NEWVEC (struct ps_fmt_field, fmt->num_fields); + char *src = malloc (fmt->src_len); + + if (!new || !fields || !src) + { + if (new) + free (new); + if (fields) + free (fields); + if (src) + free (src); + return ENOMEM; + } + + bcopy (fmt->src, src, fmt->src_len); + bcopy (fmt->fields, fields, fmt->num_fields * sizeof (struct ps_fmt_field)); + + new->fields = fields; + new->num_fields = fmt->num_fields; + new->src = src; + new->src_len = fmt->src_len; + new->inapp = fmt->inapp; + new->error = fmt->error; + *copy = new; + + return 0; +} + +/* Write an appropiate header line for FMT, containing the titles of all its + fields appropiately aligned with where the values would be printed, to + STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t +ps_fmt_write_titles (struct ps_fmt *fmt, struct ps_stream *stream) +{ + error_t err = 0; + struct ps_fmt_field *field = ps_fmt_fields (fmt); + int left = ps_fmt_num_fields (fmt); + + while (left-- > 0 && !err) + { + const char *pfx = ps_fmt_field_prefix (field); + int pfx_len = ps_fmt_field_prefix_length (field); + + if (pfx_len > 0) + err = ps_stream_write (stream, pfx, pfx_len); + + if (ps_fmt_field_fmt_spec (field) != NULL && !err) + { + const char *title = ps_fmt_field_title (field) ?: "??"; + int width = ps_fmt_field_width (field); + + if (field->flags & PS_FMT_FIELD_UPCASE_TITLE) + { + int len = strlen (title), i; + char upcase_title[len + 1]; + for (i = 0; i < len; i++) + upcase_title[i] = toupper (title[i]); + upcase_title[len] = '\0'; + err = ps_stream_write_field (stream, upcase_title, width); + } + else + err = ps_stream_write_field (stream, title, width); + } + + field++; + } + + return err; +} + +/* Format a description as instructed by FMT, of the process described by PS + to STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t +ps_fmt_write_proc_stat (struct ps_fmt *fmt, struct proc_stat *ps, struct ps_stream *stream) +{ + error_t err = 0; + struct ps_fmt_field *field = ps_fmt_fields (fmt); + int nfields = ps_fmt_num_fields (fmt); + ps_flags_t have = ps->flags; + ps_flags_t inapp = ps->inapp; + + while (nfields-- > 0 && !err) + { + const struct ps_fmt_spec *spec = ps_fmt_field_fmt_spec (field); + const char *pfx = ps_fmt_field_prefix (field); + int pfx_len = ps_fmt_field_prefix_length (field); + + if (pfx_len > 0) + err = ps_stream_write (stream, pfx, pfx_len); + + if (spec != NULL && !err) + { + ps_flags_t need = ps_getter_needs (ps_fmt_spec_getter (spec)); + + /* do we have the resources to print this field? */ + if ((need & have) == need) + /* Yup */ + err = (*spec->output_fn) (ps, field, stream); + else if (need & ~have & inapp) + /* This field is inappropriate for PS. */ + err = + ps_stream_write_field (stream, fmt->inapp ?: "", field->width); + else + /* This field is appropriate, but couldn't be fetched. */ + err = + ps_stream_write_field (stream, fmt->error ?: "", field->width); + } + + field++; + } + + return err; +} + +/* Remove those fields from FMT for which the function FN, when called on the + field, returns true. Appropiate inter-field characters are also removed: + those *following* deleted fields at the beginning of the fmt, and those + *preceeding* deleted fields *not* at the beginning. */ +void +ps_fmt_squash (struct ps_fmt *fmt, int (*fn)(struct ps_fmt_field *field)) +{ + int nfields = fmt->num_fields; + struct ps_fmt_field *fields = fmt->fields, *field = fields; + /* As we're removing some fields, we must recalculate the set of ps flags + needed by all fields. */ + ps_flags_t need = 0; + + while ((field - fields) < nfields) + if (field->spec != NULL && (*fn)(field)) + /* Squash this field! */ + { + /* Save the old prefix, in case we're deleting the first field, + and need to prepend it to the next field. */ + const char *beg_pfx = field->pfx; + int beg_pfx_len = field->pfx_len; + + nfields--; + + /* Shift down all following fields over this one. */ + if (nfields > 0) + bcopy (field + 1, field, + (nfields - (field - fields)) * sizeof *field); + + if (field == fields) + /* This is the first field, so move its prefix to the + following field (overwriting that field's prefix). This + is to ensure that the beginning of the format string is + preserved in preference to the middle, as it is more + likely to be significant. */ + { + if (nfields == 0) + /* no following fields, so just make a new end field (we're + sure to have room, as we just vacated a space). */ + { + nfields++; + field->pfx = beg_pfx; + field->pfx_len = beg_pfx_len; + field->spec = NULL; + } + else if (field->spec == NULL) + /* One following field with only a prefix -- the suffix + of the format string. Tack the prefix on before the + suffix so we preserve both the beginning and the end + of the format string. We know there's space in our + copy of the source string, because we've just squashed + a field which took at least that much room (as it + previously contained the same prefix). */ + { + field->pfx -= beg_pfx_len; + field->pfx_len += beg_pfx_len; + bcopy (beg_pfx, (char *)field->pfx, beg_pfx_len); + } + else + /* otherwise just replace the next field's prefix with + the beginning one */ + { + field->pfx = beg_pfx; + field->pfx_len = beg_pfx_len; + } + } + } + else + /* don't squash this field, just move to the next one */ + { + if (field->spec) + need |= ps_getter_needs (field->spec->getter); + field++; + } + + fmt->num_fields = nfields; + fmt->needs = need; +} + +/* Remove those fields from FMT which would need the proc_stat flags FLAGS. + Appropiate inter-field characters are also removed: those *following* + deleted fields at the beginning of the fmt, and those *preceeding* deleted + fields *not* at the beginning. */ +void +ps_fmt_squash_flags (struct ps_fmt *fmt, ps_flags_t flags) +{ + int squashable_field (struct ps_fmt_field *field) + { + return field->spec->getter->needs & flags; + } + + ps_fmt_squash (fmt, squashable_field); +} + +/* Try and restrict the number of output columns in FMT to WIDTH. */ +void +ps_fmt_set_output_width (struct ps_fmt *fmt, int width) +{ + struct ps_fmt_field *field = ps_fmt_fields (fmt); + int nfields = ps_fmt_num_fields (fmt); + + /* We're not very clever about this -- just add up the width of all the + fields but the last, and if the last has no existing width (as is + the case in most output formats), give it whatever is left over. */ + while (--nfields > 0) + { + int fw = field->width; + width -= field->pfx_len + (fw < 0 ? -fw : fw); + field++; + } + if (nfields == 0 && field->width == 0 && width > 0) + field->width = width - field->pfx_len - 1; /* 1 for the CR. */ +} diff --git a/libps/host.c b/libps/host.c new file mode 100644 index 00000000..26be9a31 --- /dev/null +++ b/libps/host.c @@ -0,0 +1,112 @@ +/* Routines to get global host info. + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "ps.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ + +/* + The Basic & Sched info types are pretty static, so we cache them, but load + info is dynamic so we don't cache that. + + See <mach/host_info.h> for information on the data types these routines + return. +*/ + +/* Return the current host port. */ +host_t +ps_get_host () +{ + static host_t host = MACH_PORT_NULL; + if (host == MACH_PORT_NULL) + host = mach_host_self (); + return host; +} + +/* Return a pointer to basic info about the current host in INFO. Since + this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t +ps_host_basic_info (host_basic_info_t *info) +{ + int initialized = FALSE; + static host_basic_info_data_t buf; + + if (!initialized) + { + int size = sizeof (buf); + error_t err = host_info (ps_get_host (), HOST_BASIC_INFO, + (host_info_t) &buf, &size); + if (err) + return err; + initialized = TRUE; + } + + *info = &buf; + return 0; +} + +/* Return a pointer to scheduling info about the current host in INFO. + Since this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t +ps_host_sched_info (host_sched_info_t *info) +{ + int initialized = FALSE; + static host_sched_info_data_t buf; + + if (!initialized) + { + int size = sizeof (buf); + error_t err = host_info (ps_get_host (), HOST_SCHED_INFO, + (host_info_t) &buf, &size); + if (err) + return err; + initialized = TRUE; + } + + *info = &buf; + return 0; +} + +/* Return a pointer to load info about the current host in INFO. Since + this is global information we just use a static buffer (if someone desires + to keep old load info, they should copy the buffer we return a pointer + to). If a system error occurs, the error code is returned, otherwise 0. */ +error_t +ps_host_load_info (host_load_info_t *info) +{ + static host_load_info_data_t buf; + int size = sizeof (buf); + error_t err = host_info (ps_get_host (), HOST_LOAD_INFO, + (host_info_t) &buf, &size); + + if (err) + return err; + + *info = &buf; + return 0; +} diff --git a/libps/proclist.c b/libps/proclist.c new file mode 100644 index 00000000..2dfcfe7d --- /dev/null +++ b/libps/proclist.c @@ -0,0 +1,692 @@ +/* The type proc_stat_list_t, which holds lists of proc_stats. + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include "ps.h" +#include "common.h" + +/* Creates a new proc_stat_list_t for processes from CONTEXT, which is + returned in PP, and returns 0, or else returns ENOMEM if there wasn't + enough memory. */ +error_t +proc_stat_list_create (struct ps_context *context, struct proc_stat_list **pp) +{ + *pp = NEW (struct proc_stat_list); + if (*pp == NULL) + return ENOMEM; + + (*pp)->proc_stats = 0; + (*pp)->num_procs = 0; + (*pp)->alloced = 0; + (*pp)->context = context; + + return 0; +} + +/* Free PP, and any resources it consumes. */ +void +proc_stat_list_free (struct proc_stat_list *pp) +{ + proc_stat_list_remove_threads (pp); + FREE (pp->proc_stats); + FREE (pp); +} + +/* Returns a copy of PP in COPY, or an error. */ +error_t +proc_stat_list_clone (struct proc_stat_list *pp, struct proc_stat_list **copy) +{ + struct proc_stat_list *new = NEW (struct proc_stat_list); + struct proc_stat **procs = NEWVEC (struct proc_stat *, pp->num_procs); + + if (!new || !procs) + { + if (new) + free (new); + if (procs) + free (procs); + return ENOMEM; + } + + bcopy (pp->proc_stats, procs, pp->num_procs); + + new->proc_stats = procs; + new->num_procs = pp->num_procs; + new->alloced = pp->num_procs; + new->context = pp->context; + *copy = new; + + return 0; +} + +/* Make sure there are at least AMOUNT new locations allocated in PP's + proc_stat array (but without changing NUM_PROCS). Returns ENOMEM if a + memory allocation error occurred, 0 otherwise. */ +static error_t +proc_stat_list_grow (struct proc_stat_list *pp, int amount) +{ + amount += pp->num_procs; + + if (amount > pp->alloced) + { + struct proc_stat **new_procs = + GROWVEC (pp->proc_stats, struct proc_stat *, amount); + + if (new_procs == NULL) + return ENOMEM; + + pp->alloced = amount; + pp->proc_stats = new_procs; + } + + return 0; +} + +/* Add proc_stat entries to PP for each process with a process id in the + array PIDS (where NUM_PROCS is the length of PIDS). Entries are only + added for processes not already in PP. ENOMEM is returned if a memory + allocation error occurs, otherwise 0. PIDs is not referenced by the + resulting proc_stat_list_t, and so may be subsequently freed. If + PROC_STATS is non-NULL, a malloced array NUM_PROCS entries long of the + resulting proc_stats is returned in it. */ +error_t +proc_stat_list_add_pids (struct proc_stat_list *pp, + pid_t *pids, unsigned num_procs, + struct proc_stat ***proc_stats) +{ + error_t err = proc_stat_list_grow (pp, num_procs); + + if (err) + return err; + else + { + int i; + struct proc_stat **end = pp->proc_stats + pp->num_procs; + + if (proc_stats) + *proc_stats = NEWVEC (struct proc_stat *, num_procs); + + for (i = 0; i < num_procs; i++) + { + int pid = *pids++; + struct proc_stat *ps = proc_stat_list_pid_proc_stat (pp, pid); + + if (ps == NULL) + { + err = ps_context_find_proc_stat (pp->context, pid, end); + if (err) + { + if (proc_stats) + free (*proc_stats); + return err; + } + else + ps = *end++; + } + + if (proc_stats) + (*proc_stats)[i] = ps; + } + + pp->num_procs = end - pp->proc_stats; + + return 0; + } +} + +/* Add a proc_stat for the process designated by PID at PP's proc context to + PP. If PID already has an entry in PP, nothing is done. If a memory + allocation error occurs, ENOMEM is returned, otherwise 0. If PS is + non-NULL, the resulting entry is returned in it. */ +error_t +proc_stat_list_add_pid (struct proc_stat_list *pp, pid_t pid, struct proc_stat **ps) +{ + struct proc_stat *_ps = proc_stat_list_pid_proc_stat (pp, pid); + + if (_ps == NULL) + { + error_t err; + + if (pp->num_procs == pp->alloced) + { + err = proc_stat_list_grow (pp, 32); + if (err) + return err; + } + + err = ps_context_find_proc_stat (pp->context, pid, &_ps); + if (err) + return err; + + pp->proc_stats[pp->num_procs++] = _ps; + } + + if (ps) + *ps = _ps; + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Returns the proc_stat in PP with a process-id of PID, if there's one, + otherwise, NULL. */ +struct proc_stat * +proc_stat_list_pid_proc_stat (struct proc_stat_list *pp, pid_t pid) +{ + unsigned nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + + while (nprocs-- > 0) + if (proc_stat_pid (*procs) == pid) + return *procs; + else + procs++; + + return NULL; +} + +/* ---------------------------------------------------------------- */ + +/* Adds all proc_stats in MERGEE to PP that don't correspond to processes + already in PP; the resulting order of proc_stats in PP is undefined. + If MERGEE and PP point to different proc contexts, EINVAL is returned. If a + memory allocation error occurs, ENOMEM is returned. Otherwise 0 is + returned, and MERGEE is freed. */ +error_t +proc_stat_list_merge (struct proc_stat_list *pp, struct proc_stat_list *mergee) +{ + if (pp->context != mergee->context) + return EINVAL; + else + { + /* Make sure there's room for the max number of new elements in PP. */ + error_t err = proc_stat_list_grow (pp, mergee->num_procs); + + if (err) + return err; + else + { + int mnprocs = mergee->num_procs; + struct proc_stat **mprocs = mergee->proc_stats; + int nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + + /* Transfer over any proc_stats from MERGEE to PP that don't + already exist there; for each of these, we set its entry in + MERGEE's proc_stat array to NULL, which prevents + proc_list_free () from freeing them. */ + while (mnprocs-- > 0) + if (proc_stat_list_pid_proc_stat(pp, proc_stat_pid (mprocs[mnprocs])) + == NULL) + { + procs[nprocs++] = mprocs[mnprocs]; + mprocs[mnprocs] = NULL; + } + + proc_stat_list_free (mergee); + + return 0; + } + } +} + +/* ---------------------------------------------------------------- */ + +/* the number of max number pids that will fit in our static buffers (above + which mig will vm_allocate space for them) */ +#define STATICPIDS 200 + +/* Add to PP entries for all processes in the collection fetched from the + proc server by the function FETCH_FN. If an error occurs, the system + error code is returned, otherwise 0. If PROC_STATS and NUM_PROCS are + non-NULL, a malloced vector of the resulting entries is returned in them. */ +static error_t +proc_stat_list_add_fn_pids (struct proc_stat_list *pp, + kern_return_t (*fetch_fn)(process_t proc, + pid_t **pids, + unsigned *num_pids), + struct proc_stat ***proc_stats, unsigned *num_procs) +{ + error_t err; + pid_t pid_array[STATICPIDS], *pids = pid_array; + unsigned num_pids = STATICPIDS; + + err = (*fetch_fn)(ps_context_server (pp->context), &pids, &num_pids); + if (err) + return err; + + err = proc_stat_list_add_pids (pp, pids, num_pids, proc_stats); + if (!err && num_procs) + *num_procs = num_pids; + + if (pids != pid_array) + VMFREE(pids, sizeof (pid_t) * num_pids); + + return err; +} + +/* Add to PP entries for all processes in the collection fetched from the + proc server by the function FETCH_FN and ID. If an error occurs, the + system error code is returned, otherwise 0. If PROC_STATS and NUM_PROCS + are non-NULL, a malloced vector of the resulting entries is returned in + them. */ +static error_t +proc_stat_list_add_id_fn_pids (struct proc_stat_list *pp, unsigned id, + kern_return_t (*fetch_fn)(process_t proc, + pid_t id, + pid_t **pids, + unsigned *num_pids), + struct proc_stat ***proc_stats, unsigned *num_procs) +{ + error_t id_fetch_fn (process_t proc, pid_t **pids, unsigned *num_pids) + { + return (*fetch_fn)(proc, id, pids, num_pids); + } + return proc_stat_list_add_fn_pids (pp, id_fetch_fn, proc_stats, num_procs); +} + +/* ---------------------------------------------------------------- */ + +/* Add to PP entries for all processes at its context. If an error occurs, + the system error code is returned, otherwise 0. If PROC_STATS and + NUM_PROCS are non-NULL, a malloced vector of the resulting entries is + returned in them. */ +error_t +proc_stat_list_add_all (struct proc_stat_list *pp, + struct proc_stat ***proc_stats, unsigned *num_procs) +{ + return + proc_stat_list_add_fn_pids (pp, proc_getallpids, proc_stats, num_procs); +} + +/* Add to PP entries for all processes in the login collection LOGIN_ID at + its context. If an error occurs, the system error code is returned, + otherwise 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector + of the resulting entries is returned in them. */ +error_t +proc_stat_list_add_login_coll (struct proc_stat_list *pp, pid_t login_id, + struct proc_stat ***proc_stats, unsigned *num_procs) +{ + return + proc_stat_list_add_id_fn_pids (pp, login_id, proc_getloginpids, + proc_stats, num_procs); +} + +/* Add to PP entries for all processes in the session SESSION_ID at its + context. If an error occurs, the system error code is returned, otherwise + 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the + resulting entries is returned in them. */ +error_t +proc_stat_list_add_session (struct proc_stat_list *pp, pid_t session_id, + struct proc_stat ***proc_stats, unsigned *num_procs) +{ + return + proc_stat_list_add_id_fn_pids (pp, session_id, proc_getsessionpids, + proc_stats, num_procs); +} + +/* Add to PP entries for all processes in the process group PGRP at its + context. If an error occurs, the system error code is returned, otherwise + 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the + resulting entries is returned in them. */ +error_t +proc_stat_list_add_pgrp (struct proc_stat_list *pp, pid_t pgrp, + struct proc_stat ***proc_stats, unsigned *num_procs) +{ + return + proc_stat_list_add_id_fn_pids (pp, pgrp, proc_getpgrppids, + proc_stats, num_procs); +} + +/* ---------------------------------------------------------------- */ + +/* Try to set FLAGS in each proc_stat in PP (but they may still not be set + -- you have to check). If a fatal error occurs, the error code is + returned, otherwise 0. */ +error_t +proc_stat_list_set_flags (struct proc_stat_list *pp, ps_flags_t flags) +{ + unsigned nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + + while (nprocs-- > 0) + { + struct proc_stat *ps = *procs++; + + if (!proc_stat_has (ps, flags)) + { + error_t err = proc_stat_set_flags (ps, flags); + if (err) + return err; + } + } + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Destructively modify PP to only include proc_stats for which the + function PREDICATE returns true; if INVERT is true, only proc_stats for + which PREDICATE returns false are kept. FLAGS is the set of pstat_flags + that PREDICATE requires be set as precondition. Regardless of the value + of INVERT, all proc_stats for which the predicate's preconditions can't + be satisfied are kept. If a fatal error occurs, the error code is + returned, it returns 0. */ +error_t +proc_stat_list_filter1(struct proc_stat_list *pp, + int (*predicate)(struct proc_stat *ps), ps_flags_t flags, + int invert) +{ + unsigned which = 0; + unsigned num_procs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + /* We compact the proc array as we filter, and KEPT points to end of the + compacted part that we've already processed. */ + struct proc_stat **kept = procs; + error_t err = proc_stat_list_set_flags (pp, flags); + + if (err) + return err; + + invert = !!invert; /* Convert to a boolean. */ + + while (which < num_procs) + { + struct proc_stat *ps = procs[which++]; + + /* See if we should keep PS; if PS doesn't satisfy the set of flags we + need, we don't attempt to call PREDICATE at all, and keep PS. */ + + if (!proc_stat_has(ps, flags) || !!predicate (ps) != invert) + *kept++ = ps; + /* ... otherwise implicitly delete PS from PP by not putting it in the + KEPT sequence. */ + } + + pp->num_procs = kept - procs; + + return 0; +} + +/* Destructively modify PP to only include proc_stats for which the + predicate function in FILTER returns true; if INVERT is true, only + proc_stats for which the predicate returns false are kept. Regardless + of the value of INVERT, all proc_stats for which the predicate's + preconditions can't be satisfied are kept. If a fatal error occurs, + the error code is returned, it returns 0. */ +error_t +proc_stat_list_filter (struct proc_stat_list *pp, + const struct ps_filter *filter, int invert) +{ + return + proc_stat_list_filter1(pp, + ps_filter_predicate (filter), + ps_filter_needs (filter), + invert); +} + +/* ---------------------------------------------------------------- */ + +/* Destructively sort proc_stats in PP by ascending value of the field + returned by GETTER, and compared by CMP_FN; If REVERSE is true, use the + opposite order. If a fatal error occurs, the error code is returned, it + returns 0. */ +error_t +proc_stat_list_sort1 (struct proc_stat_list *pp, + const struct ps_getter *getter, + int (*cmp_fn)(struct proc_stat *ps1, + struct proc_stat *ps2, + const struct ps_getter *getter), + int reverse) +{ + int needs = ps_getter_needs (getter); + struct proc_stat **procs = pp->proc_stats; + error_t err = proc_stat_list_set_flags (pp, needs); + + /* Lessp is a nested function so it may use state variables from + proc_stat_list_sort1, which qsort gives no other way of passing in. */ + int lessp (const void *p1, const void *p2) + { + struct proc_stat *ps1 = *(struct proc_stat **)p1; + struct proc_stat *ps2 = *(struct proc_stat **)p2; + int is_th_1 = proc_stat_is_thread (ps1); + int is_th_2 = proc_stat_is_thread (ps2); + + if (!is_th_1 || !is_th_2 + || proc_stat_thread_origin(ps1) != proc_stat_thread_origin (ps2)) + /* Compare the threads' origins to keep them ordered after their + respective processes. The exception is when they're both from the + same process, in which case we want to compare them directly so that + a process's threads are sorted among themselves (in most cases this + just fails because the thread doesn't have the proper fields; this + should just result in the threads remaining in their original + order). */ + { + if (is_th_1) + ps1 = proc_stat_thread_origin (ps1); + if (is_th_2) + ps2 = proc_stat_thread_origin (ps2); + } + + if (ps1 == ps2 || !proc_stat_has(ps1, needs) || !proc_stat_has (ps2, needs)) + /* If we're comparing a thread with it's process (and so the thread's + been replaced by the process), or we can't call CMP_FN on either + proc_stat due to lack of the necessary preconditions, then compare + their original positions, to retain the same order. */ + return p1 - p2; + else if (reverse) + return cmp_fn (ps2, ps1, getter); + else + return cmp_fn (ps1, ps2, getter); + } + + if (err) + return err; + + qsort((void *)procs, (size_t)pp->num_procs, sizeof (struct proc_stat *), lessp); + + return 0; +} + +/* Destructively sort proc_stats in PP by ascending value of the field KEY; + if REVERSE is true, use the opposite order. If KEY isn't a valid sort + key, EINVAL is returned. If a fatal error occurs the error code is + returned. Otherwise, 0 is returned. */ +error_t +proc_stat_list_sort (struct proc_stat_list *pp, + const struct ps_fmt_spec *key, int reverse) +{ + int (*cmp_fn)() = ps_fmt_spec_compare_fn (key); + if (cmp_fn == NULL) + return EINVAL; + else + return + proc_stat_list_sort1 (pp, ps_fmt_spec_getter (key), cmp_fn, reverse); +} + +/* ---------------------------------------------------------------- */ + +/* Format a description as instructed by FMT, of the processes in PP to + STREAM, separated by newlines (and with a terminating newline). If a + fatal error occurs, the error code is returned, otherwise 0. */ +error_t +proc_stat_list_fmt (struct proc_stat_list *pp, struct ps_fmt *fmt, + struct ps_stream *stream) +{ + unsigned nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + error_t err = proc_stat_list_set_flags(pp, ps_fmt_needs (fmt)); + + while (!err && nprocs-- > 0) + { + err = ps_fmt_write_proc_stat (fmt, *procs++, stream); + if (! err) + ps_stream_newline (stream); + } + + return err; +} + +/* ---------------------------------------------------------------- */ + +/* Modifies FLAGS to be the subset which can't be set in any proc_stat in + PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat + as possible). If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t +proc_stat_list_find_bogus_flags (struct proc_stat_list *pp, ps_flags_t *flags) +{ + unsigned nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + error_t err = proc_stat_list_set_flags (pp, *flags); + + if (err) + return err; + + while (nprocs-- > 0 && *flags != 0) + *flags &= ~proc_stat_flags (*procs++); + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Add thread entries for for every process in PP, located immediately after + the containing process in sequence. Subsequent sorting of PP will leave + the thread entries located after the containing process, although the + order of the thread entries themselves may change. If a fatal error + occurs, the error code is returned, otherwise 0. */ +error_t +proc_stat_list_add_threads (struct proc_stat_list *pp) +{ + error_t err = proc_stat_list_set_flags (pp, PSTAT_NUM_THREADS); + + if (err) + return err; + else + { + int new_entries = 0; + int nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + + /* First, count the number of threads that will be added. */ + while (nprocs-- > 0) + { + struct proc_stat *ps = *procs++; + if (proc_stat_has (ps, PSTAT_NUM_THREADS)) + new_entries += proc_stat_num_threads (ps); + } + + /* And make room for them... */ + err = proc_stat_list_grow (pp, new_entries); + if (err) + return err; + else + /* Now add thread entries for every existing entry in PP; we go + through them backwards so we can do it in place. */ + { + struct proc_stat **end = pp->proc_stats + pp->num_procs + new_entries; + + nprocs = pp->num_procs; + procs = pp->proc_stats + nprocs; + + while (nprocs-- > 0) + { + struct proc_stat *ps = *--procs; + if (proc_stat_has (ps, PSTAT_NUM_THREADS)) + { + int nthreads = proc_stat_num_threads (ps); + while (nthreads-- > 0) + proc_stat_thread_create (ps, nthreads, --end); + } + *--end = ps; + } + + pp->num_procs += new_entries; + } + } + + return 0; +} + +error_t +proc_stat_list_remove_threads (struct proc_stat_list *pp) +{ + int is_thread (struct proc_stat *ps) + { + return proc_stat_is_thread (ps); + } + return proc_stat_list_filter1(pp, is_thread, 0, FALSE); +} + +/* ---------------------------------------------------------------- */ + +/* Calls FN in order for each proc_stat in PP. If FN ever returns a non-zero + value, then the iteration is stopped, and the value is returned + immediately; otherwise, 0 is returned. */ +int +proc_stat_list_for_each (struct proc_stat_list *pp, int (*fn)(struct proc_stat *ps)) +{ + unsigned nprocs = pp->num_procs; + struct proc_stat **procs = pp->proc_stats; + + while (nprocs-- > 0) + { + int val = (*fn)(*procs++); + if (val) + return val; + } + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Returns true if SPEC is `nominal' in every entry in PP. */ +int +proc_stat_list_spec_nominal (struct proc_stat_list *pp, + const struct ps_fmt_spec *spec) +{ + int (*nominal_fn)(struct proc_stat *ps, const struct ps_getter *getter) = + spec->nominal_fn; + + if (nominal_fn == NULL) + return FALSE; + else + { + const struct ps_getter *getter = ps_fmt_spec_getter (spec); + ps_flags_t needs = ps_getter_needs (getter); + int interesting (struct proc_stat *ps) + { + return proc_stat_has (ps, needs) && !(*nominal_fn)(ps, getter); + } + + proc_stat_list_set_flags (pp, needs); + + return !proc_stat_list_for_each (pp, interesting); + } +} diff --git a/libps/procstat.c b/libps/procstat.c new file mode 100644 index 00000000..46058e07 --- /dev/null +++ b/libps/procstat.c @@ -0,0 +1,1137 @@ +/* The proc_stat type, which holds information about a hurd process. + + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include "ps.h" +#include "common.h" + +#include "ps_msg.h" + +/* ---------------------------------------------------------------- */ + +/* These directly correspond to the bits in a state, starting from 0. See + ps.h for an explanation of what each of these means. */ +char *proc_stat_state_tags = "TZRHDSIN<u+slfmpoxwg"; + +/* ---------------------------------------------------------------- */ + +/* The type of the per-thread data returned by proc_getprocinfo. */ +typedef typeof (((struct procinfo *)0)->threadinfos[0]) threadinfo_data_t; +typedef threadinfo_data_t *threadinfo_t; + +/* Return the PSTAT_STATE_ bits describing the state of an individual thread, + from that thread's thread_basic_info_t struct */ +static int +thread_state (thread_basic_info_t bi) +{ + int state = 0; + + switch (bi->run_state) + { + case TH_STATE_RUNNING: + state |= PSTAT_STATE_T_RUN; + break; + case TH_STATE_UNINTERRUPTIBLE: + state |= PSTAT_STATE_T_WAIT; + break; + case TH_STATE_HALTED: + state |= PSTAT_STATE_T_HALT; + break; + case TH_STATE_STOPPED: + state |= PSTAT_STATE_T_HALT | PSTAT_STATE_T_UNCLEAN; + break; + case TH_STATE_WAITING: + /* Act like unix: waits of less than 20 seconds means a process is + `sleeping' and >= 20 seconds means it's `idle' */ + state |= bi->sleep_time < 20 ? PSTAT_STATE_T_SLEEP : PSTAT_STATE_T_IDLE; + break; + } + + if (bi->base_priority < 12) + state |= PSTAT_STATE_T_NASTY; + else if (bi->base_priority > 12) + state |= PSTAT_STATE_T_NICE; + + return state; +} + +/* ---------------------------------------------------------------- */ + +/* The set of things we get from procinfo that are per-thread. */ +#define PSTAT_PROCINFO_THREAD \ + (PSTAT_THREAD_BASIC | PSTAT_THREAD_SCHED | PSTAT_THREAD_WAIT) + +/* The set of things we get from procinfo that are per-task, and thread dependent. */ +#define PSTAT_PROCINFO_TASK_THREAD_DEP \ + (PSTAT_PROCINFO_THREAD | PSTAT_NUM_THREADS | PSTAT_THREAD_WAITS) + +/* The set of things we get from procinfo that are per-task (note that this + includes thread fields, because tasks use them for thread summaries). */ +#define PSTAT_PROCINFO_TASK \ + (PSTAT_PROCINFO_TASK_THREAD_DEP | PSTAT_PROC_INFO | PSTAT_TASK_BASIC) + +/* The set of PSTAT_ flags that we get using proc_getprocinfo. */ +#define PSTAT_PROCINFO PSTAT_PROCINFO_TASK + +/* The set of things in PSTAT_PROCINFO that we will not attempt to refetch on + subsequent getprocinfo calls. */ +#define PSTAT_PROCINFO_MERGE PSTAT_TASK_BASIC +#define PSTAT_PROCINFO_REFETCH (PSTAT_PROCINFO - PSTAT_PROCINFO_MERGE) + +/* Fetches process information from the set in PSTAT_PROCINFO, returning it + in PI & PI_SIZE. NEED is the information, and HAVE is the what we already + have. */ +static error_t +fetch_procinfo (process_t server, pid_t pid, + ps_flags_t need, ps_flags_t *have, + struct procinfo **pi, size_t *pi_size, + char **waits, size_t *waits_len) +{ + int pi_flags = 0; + + if ((need & PSTAT_TASK_BASIC) && !(*have & PSTAT_TASK_BASIC)) + pi_flags |= PI_FETCH_TASKINFO; + if ((need & PSTAT_NUM_THREADS) && !(*have & PSTAT_NUM_THREADS)) + pi_flags |= PI_FETCH_THREADS; + if ((need & PSTAT_THREAD_BASIC) && !(*have & PSTAT_THREAD_BASIC)) + pi_flags |= PI_FETCH_THREAD_BASIC | PI_FETCH_THREADS; + if ((need & PSTAT_THREAD_SCHED) && !(*have & PSTAT_THREAD_SCHED)) + pi_flags |= PI_FETCH_THREAD_SCHED | PI_FETCH_THREADS; + if ((need & PSTAT_THREAD_WAITS) && !(*have & PSTAT_THREAD_WAITS)) + pi_flags |= PI_FETCH_THREAD_WAITS | PI_FETCH_THREADS; + + if (pi_flags || ((need & PSTAT_PROC_INFO) && !(*have & PSTAT_PROC_INFO))) + { + error_t err; + + *pi_size /= sizeof (int); /* getprocinfo takes an array of ints. */ + err = proc_getprocinfo (server, pid, &pi_flags, + (procinfo_t *)pi, pi_size, waits, waits_len); + *pi_size *= sizeof (int); + + if (! err) + /* Update *HAVE to reflect what we've successfully fetched. */ + { + *have |= PSTAT_PROC_INFO; + if (pi_flags & PI_FETCH_TASKINFO) + *have |= PSTAT_TASK_BASIC; + if (pi_flags & PI_FETCH_THREADS) + *have |= PSTAT_NUM_THREADS; + if (pi_flags & PI_FETCH_THREAD_BASIC) + *have |= PSTAT_THREAD_BASIC; + if (pi_flags & PI_FETCH_THREAD_SCHED) + *have |= PSTAT_THREAD_SCHED; + if (pi_flags & PI_FETCH_THREAD_WAITS) + *have |= PSTAT_THREAD_WAITS; + } + return err; + } + else + return 0; +} + +/* The size of the initial buffer malloced to try and avoid getting + vm_alloced memory for the procinfo structure returned by getprocinfo. + Here we just give enough for four threads. */ +#define PROCINFO_MALLOC_SIZE \ + (sizeof (struct procinfo) + 4 * sizeof (threadinfo_data_t)) + +#define WAITS_MALLOC_SIZE 128 + +/* Fetches process information from the set in PSTAT_PROCINFO, returning it + in PI & PI_SIZE, and if *PI_SIZE is non-zero, merges the new information + with what was in *PI, and deallocates *PI. NEED is the information, and + *HAVE is the what we already have (which will be updated). */ +static ps_flags_t +merge_procinfo (struct proc_stat *ps, ps_flags_t need, ps_flags_t have) +{ + error_t err; + struct procinfo *new_pi, old_pi_hdr; + size_t new_pi_size; + char *new_waits = 0; + size_t new_waits_len = 0; + /* We always re-fetch any thread-specific info, as the set of threads could + have changed since the last time we did this, and we can't tell. */ + ps_flags_t really_need = need | (have & PSTAT_PROCINFO_REFETCH); + ps_flags_t really_have = have & ~PSTAT_PROCINFO_REFETCH; + + /* Give NEW_PI, the default buffer to receive procinfo data, some storage. */ + if (have & PSTAT_PROCINFO) + /* We already have some procinfo stuff, so try to reuse its storage, + first saving the old values. We know that below we'll never try to + merge anything beyond the static struct procinfo header, so just save + that. */ + old_pi_hdr = *ps->proc_info; + else + /* The first time we're getting procinfo stuff. Malloc a block that's + probably big enough for everything. */ + { + ps->proc_info = malloc (PROCINFO_MALLOC_SIZE); + ps->proc_info_size = PROCINFO_MALLOC_SIZE; + ps->proc_info_vm_alloced = 0; + if (! ps->proc_info) + return ENOMEM; + } + new_pi = ps->proc_info; + new_pi_size = ps->proc_info_size; + + if (really_need & PSTAT_THREAD_WAITS) + /* We're going to get thread waits info, so make some storage for it too.*/ + { + if (! (have & PSTAT_THREAD_WAITS)) + { + ps->thread_waits = malloc (WAITS_MALLOC_SIZE); + ps->thread_waits_len = WAITS_MALLOC_SIZE; + ps->thread_waits_vm_alloced = 0; + } + new_waits = ps->thread_waits; + new_waits_len = ps->thread_waits_len; + } + + err = fetch_procinfo (ps->context->server, ps->pid, really_need, &really_have, + &new_pi, &new_pi_size, + &new_waits, &new_waits_len); + if (err) + /* Just keep what we had before. If that was nothing, we have to free + the first-time storage we malloced. */ + { + if (! (have & PSTAT_PROCINFO)) + free (new_pi); + if ((really_need & PSTAT_THREAD_WAITS) && !(have & PSTAT_THREAD_WAITS)) + free (new_waits); + return have; + } + + /* There was old information, try merging it. */ + if (have & PSTAT_TASK_BASIC) + /* Task info. */ + bcopy (&old_pi_hdr.taskinfo, &new_pi->taskinfo, + sizeof (struct task_basic_info)); + /* That's it for now. */ + + if (new_pi != ps->proc_info) + /* We got new memory vm_alloced by the getprocinfo, discard the old. */ + { + if (ps->proc_info_vm_alloced) + munmap (ps->proc_info, ps->proc_info_size); + else + free (ps->proc_info); + ps->proc_info = new_pi; + ps->proc_info_size = new_pi_size; + ps->proc_info_vm_alloced = 1; + } + + if (really_need & PSTAT_THREAD_WAITS) + /* We were trying to get thread waits.... */ + { + if (! (really_have & PSTAT_THREAD_WAITS)) + /* Failed to do so. Make sure we deallocate memory for it. */ + new_waits = 0; + if (new_waits != ps->thread_waits) + /* We got new memory vm_alloced by the getprocinfo, discard the old. */ + { + if (ps->thread_waits_vm_alloced) + munmap (ps->thread_waits, ps->thread_waits_len); + else + free (ps->thread_waits); + ps->thread_waits = new_waits; + ps->thread_waits_len = new_waits_len; + ps->thread_waits_vm_alloced = 1; + } + } + + /* Return what thread info we have -- this may *decrease*, as all + previously fetched thread-info is out-of-date now, so we have to make do + with whatever we've fetched this time. */ + return really_have; +} + +/* Returns FLAGS augmented with any other flags that are necessary + preconditions to setting them. */ +static ps_flags_t +add_preconditions (ps_flags_t flags, struct ps_context *context) +{ + /* Implement any inter-flag dependencies: if the new flags in FLAGS depend on + some other set of flags to be set, make sure those are also in FLAGS. */ + + if ((flags & PSTAT_USER_MASK) + && context->user_hooks && context->user_hooks->dependencies) + /* There are some user flags needing to be set... See what they need. */ + flags |= (*context->user_hooks->dependencies) (flags & PSTAT_USER_MASK); + + if (flags & PSTAT_TTY) + flags |= PSTAT_CTTYID; + if (flags & PSTAT_STATE) + flags |= PSTAT_PROC_INFO | PSTAT_THREAD_BASIC; + if (flags & PSTAT_OWNER) + flags |= PSTAT_OWNER_UID; + if (flags & PSTAT_OWNER_UID) + flags |= PSTAT_PROC_INFO; + if (flags & PSTAT_SUSPEND_COUNT) + /* We just request the resources require for both the thread and task + versions, as the extraneous info won't be possible to aquire anyway. */ + flags |= PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC; + if (flags & (PSTAT_CTTYID | PSTAT_CWDIR | PSTAT_AUTH | PSTAT_UMASK) + && !(flags & PSTAT_NO_MSGPORT)) + { + flags |= PSTAT_MSGPORT; + flags |= PSTAT_TASK; /* for authentication */ + } + if (flags & PSTAT_TASK_EVENTS) + flags |= PSTAT_TASK; + + return flags; +} + +/* Those flags that should be set before calling should_suppress_msgport. */ +#define PSTAT_TEST_MSGPORT \ + (PSTAT_NUM_THREADS | PSTAT_SUSPEND_COUNT | PSTAT_THREAD_BASIC) + +/* Those flags that need the msg port, perhaps implicitly. */ +#define PSTAT_USES_MSGPORT \ + (PSTAT_MSGPORT | PSTAT_THREAD_WAIT | PSTAT_THREAD_WAITS) + +/* Return true when there's some condition indicating that we shouldn't use + PS's msg port. For this routine to work correctly, PS's flags should + contain as many flags in PSTAT_TEST_MSGPORT as possible. */ +static int +should_suppress_msgport (struct proc_stat *ps) +{ + ps_flags_t have = ps->flags; + + if ((have & PSTAT_SUSPEND_COUNT) && ps->suspend_count != 0) + /* Task is suspended. */ + return TRUE; + + if ((have & PSTAT_THREAD_BASIC) && ps->thread_basic_info->suspend_count != 0) + /* All threads are suspended. */ + return TRUE; + + if ((have & PSTAT_NUM_THREADS) && ps->num_threads == 0) + /* No threads (some bug could cause the procserver still to think there's + a msgport). */ + return TRUE; + + return FALSE; +} + +/* Returns FLAGS with PSTAT_MSGPORT turned off and PSTAT_NO_MSGPORT on. */ +#define SUPPRESS_MSGPORT_FLAGS(flags) \ + (((flags) & ~PSTAT_USES_MSGPORT) | PSTAT_NO_MSGPORT) + +/* ---------------------------------------------------------------- */ + +/* Returns a new malloced struct thread_basic_info containing a summary of + all the thread basic info in PI. Sizes and cumulative times are summed, + delta time are averaged. The run_states are added by having running + thread take precedence over waiting ones, and if there are any other + incompatible states, simply using a bogus value of -1. */ +static struct thread_basic_info * +summarize_thread_basic_info (struct procinfo *pi) +{ + int i; + unsigned num_threads = 0, num_run_threads = 0; + thread_basic_info_t tbi = malloc (sizeof (struct thread_basic_info)); + int run_base_priority = 0, run_cur_priority = 0; + int total_base_priority = 0, total_cur_priority = 0; + + if (!tbi) + return 0; + + bzero (tbi, sizeof *tbi); + + for (i = 0; i < pi->nthreads; i++) + if (! pi->threadinfos[i].died + && ! (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE)) + { + thread_basic_info_t bi = &pi->threadinfos[i].pis_bi; + int thread_run_state = bi->run_state; + + /* Construct some aggregate run-state for the process: besides the + defined thread run_states, we use 0 to mean no threads, and -1 + to mean two threads have conflicting run_stats. */ + + if (tbi->run_state == 0) + /* No prior state, just copy this thread's. */ + tbi->run_state = thread_run_state; + else if (tbi->run_state == TH_STATE_RUNNING + || thread_run_state == TH_STATE_RUNNING) + /* If any thread is running, mark the process as running. */ + tbi->run_state = TH_STATE_RUNNING; + else if (tbi->run_state != bi->run_state) + /* Otherwise there are two conflicting non-running states, so + just give up and say we don't know. */ + tbi->run_state = -1; + + tbi->cpu_usage += bi->cpu_usage; + tbi->sleep_time += bi->sleep_time; + + /* The aggregate suspend count is the minimum of all threads. */ + if (i == 0 || tbi->suspend_count > bi->suspend_count) + tbi->suspend_count = bi->suspend_count; + + tbi->user_time.seconds += bi->user_time.seconds; + tbi->user_time.microseconds += bi->user_time.microseconds; + tbi->system_time.seconds += bi->system_time.seconds; + tbi->system_time.microseconds += bi->system_time.microseconds; + + if (tbi->run_state == TH_STATE_RUNNING) + { + run_base_priority += bi->base_priority; + run_cur_priority += bi->base_priority; + num_run_threads++; + } + else + { + total_base_priority += bi->base_priority; + total_cur_priority += bi->base_priority; + } + + num_threads++; + } + + if (num_threads > 0) + { + tbi->sleep_time /= num_threads; + if (num_run_threads > 0) + { + tbi->base_priority = run_base_priority / num_run_threads; + tbi->cur_priority = run_cur_priority / num_run_threads; + } + else + { + tbi->base_priority = total_base_priority / num_threads; + tbi->cur_priority = total_cur_priority / num_threads; + } + } + + tbi->user_time.seconds += tbi->user_time.microseconds / 1000000; + tbi->user_time.microseconds %= 1000000; + tbi->system_time.seconds += tbi->system_time.microseconds / 1000000; + tbi->system_time.microseconds %= 1000000; + + return tbi; +} + +/* Returns a new malloced struct thread_sched_info containing a summary of + all the thread scheduling info in PI. The prioritys are an average of the + thread priorities. */ +static struct thread_sched_info * +summarize_thread_sched_info (struct procinfo *pi) +{ + int i; + unsigned num_threads = 0; + thread_sched_info_t tsi = malloc (sizeof (struct thread_sched_info)); + + if (!tsi) + return 0; + + bzero (tsi, sizeof *tsi); + + for (i = 0; i < pi->nthreads; i++) + if (! pi->threadinfos[i].died + && ! (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE)) + { + thread_sched_info_t si = &pi->threadinfos[i].pis_si; + tsi->base_priority += si->base_priority; + tsi->cur_priority += si->cur_priority; + tsi->max_priority += si->max_priority; + tsi->depress_priority += si->depress_priority; + num_threads++; + } + + if (num_threads > 0) + { + tsi->base_priority /= num_threads; + tsi->cur_priority /= num_threads; + tsi->max_priority /= num_threads; + tsi->depress_priority /= num_threads; + } + + return tsi; +} + +/* Returns the union of the state bits for all the threads in PI. */ +static int +summarize_thread_states (struct procinfo *pi) +{ + int i; + int state = 0; + + /* The union of all thread state bits... */ + for (i = 0; i < pi->nthreads; i++) + if (! pi->threadinfos[i].died + && ! (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE)) + state |= thread_state (&pi->threadinfos[i].pis_bi); + + return state; +} + +/* Returns what's blocking the first blocked thread in PI in WAIT and RPC. */ +static void +summarize_thread_waits (struct procinfo *pi, char *waits, size_t waits_len, + char **wait, int *rpc) +{ + int i; + char *next_wait = waits; + + *wait = 0; /* Defaults */ + *rpc = 0; + + for (i = 0; i < pi->nthreads; i++) + if (! pi->threadinfos[i].died) + { + if (next_wait > waits + waits_len) + break; + else + { + int left = waits + waits_len - next_wait; + + if (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE) + ; /* kernel idle thread; ignore */ + else if (strncmp (next_wait, "msgport", left) == 0 + || strncmp (next_wait, "itimer", left) == 0) + ; /* libc internal threads; ignore. */ + else if (*wait) + /* There are multiple user threads. Punt. */ + { + *wait = "*"; + *rpc = 0; + break; + } + else + { + *wait = next_wait; + *rpc = pi->threadinfos[i].rpc_block; + } + + /* Advance NEXT_WAIT to the next wait string. */ + next_wait += strnlen (next_wait, left) + 1; + } + } +} + +/* Returns the number of threads in PI that aren't marked dead. */ +static unsigned +count_threads (struct procinfo *pi, ps_flags_t have) +{ + if (have & (PSTAT_PROCINFO_TASK_THREAD_DEP & ~PSTAT_NUM_THREADS)) + /* If we have thread info besides the number of threads, then the + threadinfos structures in PI are valid (we use the died bit). */ + { + int i; + unsigned num_threads = 0; + + /* The union of all thread state bits... */ + for (i = 0; i < pi->nthreads; i++) + if (! pi->threadinfos[i].died) + num_threads++; + + return num_threads; + } + else + /* Otherwise just use the number proc gave us. */ + return pi->nthreads; +} + +/* Returns the threadinfo for the INDEX'th thread from PI that isn't marked + dead. */ +threadinfo_t +get_thread_info (struct procinfo *pi, unsigned index) +{ + int i; + + /* The union of all thread state bits... */ + for (i = 0; i < pi->nthreads; i++) + if (! pi->threadinfos[i].died && index-- == 0) + return &pi->threadinfos[i]; + + return 0; +} + +/* Returns a pointer to the Nth entry in the '\0'-separated vector of strings + in ARGZ & ARGZ_LEN. Note that we don't have to do the bit with only + counting non-dead threads like get_thread_info does, because the + thread_waits string vector only contains entries for live threads. */ +char * +get_thread_wait (char *waits, size_t waits_len, unsigned n) +{ + char *wait = waits; + while (n-- && wait) + if (wait >= waits + waits_len) + wait = 0; + else + wait += strnlen (wait, waits + waits_len - wait) + 1; + return wait; +} + +/* Returns a malloced block of memory SIZE bytes long, containing a copy of + SRC. */ +static void * +clone (void *src, size_t size) +{ + void *dst = malloc (size); + if (dst) + bcopy (src, dst, size); + return dst; +} + +/* Add the information specified by NEED to PS which we can get with + proc_getprocinfo. */ +static ps_flags_t +set_procinfo_flags (struct proc_stat *ps, ps_flags_t need, ps_flags_t have) +{ + if (have & PSTAT_PID) + { + struct procinfo *pi; + ps_flags_t had = have; + + if (! (have & PSTAT_PROCINFO)) + /* Never got any before; zero out our pointers. */ + { + ps->proc_info = 0; + ps->proc_info_size = 0; + ps->thread_waits = 0; + ps->thread_waits_len = 0; + } + + if ((need & PSTAT_THREAD_WAIT) && !(need & PSTAT_THREAD_WAITS)) + /* We need thread wait information only for summarization. This is + expensive and pointless for lots of threads, so try to avoid it + in that case. */ + { + if (! (have & PSTAT_NUM_THREADS)) + /* We've don't know how many threads there are yet; find out. */ + { + have = merge_procinfo (ps, PSTAT_NUM_THREADS, have); + if (have & PSTAT_NUM_THREADS) + ps->num_threads = count_threads (ps->proc_info, have); + } + if ((have & PSTAT_NUM_THREADS) && ps->num_threads <= 3) + /* Perhaps only 1 user thread -- thread-wait info may be + meaningful! */ + need |= PSTAT_THREAD_WAITS; + } + + have = merge_procinfo (ps, need, have); + pi = ps->proc_info; + + /* Update dependent fields. We redo these even if we've already + gotten them, as the information will be newer. */ + if (have & PSTAT_TASK_BASIC) + ps->task_basic_info = &pi->taskinfo; + if (have & PSTAT_NUM_THREADS) + ps->num_threads = count_threads (pi, have); + if (had & PSTAT_THREAD_BASIC) + free (ps->thread_basic_info); + if (have & PSTAT_THREAD_BASIC) + ps->thread_basic_info = summarize_thread_basic_info (pi); + if (had & PSTAT_THREAD_SCHED) + free (ps->thread_sched_info); + if (have & PSTAT_THREAD_SCHED) + ps->thread_sched_info = summarize_thread_sched_info (pi); + if (have & PSTAT_THREAD_WAITS) + /* Thread-waits info can be used to generate thread-wait info. */ + { + summarize_thread_waits (pi, + ps->thread_waits, ps->thread_waits_len, + &ps->thread_wait, &ps->thread_rpc); + have |= PSTAT_THREAD_WAIT; + } + else if (!(have & PSTAT_NO_MSGPORT) + && (have & PSTAT_NUM_THREADS) && ps->num_threads > 3) + /* More than 3 threads (1 user thread + libc signal thread + + possible itimer thread) always results in this value for the + process's thread_wait field. For fewer threads, we should + have fetched thread_waits info and hit the previous case. */ + { + ps->thread_wait = "*"; + ps->thread_rpc = 0; + have |= PSTAT_THREAD_WAIT; + } + } + else + /* For a thread, we get use the proc_info from the containing process. */ + { + struct proc_stat *origin = ps->thread_origin; + /* Fetch for the containing process basically the same information we + want for the thread, but it also needs all the thread wait info. */ + ps_flags_t oflags = + (need & PSTAT_PROCINFO_THREAD) + | ((need & PSTAT_THREAD_WAIT) ? PSTAT_THREAD_WAITS : 0); + + proc_stat_set_flags (origin, oflags); + oflags = origin->flags; + + if (oflags & PSTAT_PROCINFO_THREAD) + /* Got some threadinfo at least. */ + { + threadinfo_t ti = + get_thread_info (origin->proc_info, ps->thread_index); + + /* Now copy out the information for this particular thread from the + ORIGIN's list of thread information. */ + + need &= ~have; + + if ((need & PSTAT_THREAD_BASIC) && (oflags & PSTAT_THREAD_BASIC) + && (ps->thread_basic_info = + clone (&ti->pis_bi, sizeof (struct thread_basic_info)))) + have |= PSTAT_THREAD_BASIC; + + if ((need & PSTAT_THREAD_SCHED) && (oflags & PSTAT_THREAD_SCHED) + && (ps->thread_sched_info = + clone (&ti->pis_si, sizeof (struct thread_sched_info)))) + have |= PSTAT_THREAD_SCHED; + + if ((need & PSTAT_THREAD_WAIT) && (oflags & PSTAT_THREAD_WAITS)) + { + ps->thread_wait = + get_thread_wait (origin->thread_waits, + origin->thread_waits_len, + ps->thread_index); + if (ps->thread_wait) + { + ps->thread_rpc = ti->rpc_block; + have |= PSTAT_THREAD_WAIT; + } + } + } + + /* Mark things that don't apply to threads (note that we don't do the + analogous thing for tasks above, as tasks do have thread fields + containing summary information for all their threads). */ + ps->inapp |= need & ~have & PSTAT_PROCINFO & ~PSTAT_PROCINFO_THREAD; + } + + return have; +} + +/* Add FLAGS to PS's flags, fetching information as necessary to validate + the corresponding fields in PS. Afterwards you must still check the flags + field before using new fields, as something might have failed. Returns + a system error code if a fatal error occurred, or 0 if none. */ +error_t +proc_stat_set_flags (struct proc_stat *ps, ps_flags_t flags) +{ + ps_flags_t have = ps->flags; /* flags set in ps */ + ps_flags_t need; /* flags not set in ps, but desired to be */ + ps_flags_t no_msgport_flags; /* a version of FLAGS for use when we can't + use the msg port. */ + ps_flags_t test_msgport_flags; /* Flags needed to test for msgport + validity, including any preconditions. */ + process_t server = ps_context_server (ps->context); + + /* Turn off use of the msg port if we decide somewhere along the way that + it's hosed. */ + void suppress_msgport () + { + /* Turn off those things that were only good given the msg port. */ + need &= ~(flags & ~no_msgport_flags); + have = SUPPRESS_MSGPORT_FLAGS (have); + } + + flags &= ~ps->failed; /* Don't try to get things we can't. */ + + /* Propagate PSTAT_NO_MSGPORT. */ + if (flags & PSTAT_NO_MSGPORT) + have = SUPPRESS_MSGPORT_FLAGS (have); + if (have & PSTAT_NO_MSGPORT) + flags = SUPPRESS_MSGPORT_FLAGS (flags); + + no_msgport_flags = + add_preconditions (SUPPRESS_MSGPORT_FLAGS (flags), ps->context); + flags = add_preconditions (flags, ps->context); + + if (flags & PSTAT_USES_MSGPORT) + /* Add in some values that we can use to determine whether the msgport + shouldn't be used. */ + { + test_msgport_flags = add_preconditions (PSTAT_TEST_MSGPORT, ps->context); + flags |= test_msgport_flags; + } + else + test_msgport_flags = 0; + + need = flags & ~have & ~ps->failed; + + /* Returns true if (1) FLAGS is in NEED, and (2) the appropriate + preconditions PRECOND are available; if only (1) is true, FLAG is added + to the INAPP set if appropiate (to distinguish it from an error), and + returns false. */ +#define NEED(flag, precond) \ + ({ \ + ps_flags_t __flag = (flag), _precond = (precond); \ + int val; \ + if (! (__flag & need)) \ + val = 0; \ + else if ((_precond & have) == _precond) \ + val = 1; \ + else \ + { \ + val = 0; \ + if (_precond & ps->inapp) \ + ps->inapp |= __flag; \ + } \ + val; \ + }) + + /* MGET: If we're trying to set FLAG, and the preconditions PRECOND are set + in the flags already, then eval CALL and set ERR to the result. + If the resulting ERR is 0 add FLAG to the set of valid flags. ERR is + returned. */ +#define MGET(flag, precond, call) \ + ({ \ + error_t err; \ + ps_flags_t _flag = (flag); \ + if (NEED (_flag, precond)) \ + { \ + err = (call); \ + if (!err) \ + have |= _flag; \ + } \ + else \ + err = 0; \ + err; \ + }) + + /* A version of MGET specifically for the msg port, that turns off the msg + port if a call to it times out. It also implies a precondition of + PSTAT_MSGPORT. */ +#define MP_MGET(flag, precond, call) \ + ({ error_t err = MGET (flag, (precond) | PSTAT_MSGPORT, call); \ + if (err == EMACH_RCV_TIMED_OUT) suppress_msgport (); \ + err; \ + }) + + if (need & ~have & test_msgport_flags & PSTAT_PROCINFO) + /* Pre-fetch information returned by set_procinfo_flags that we need for + msgport validity testing; if we need other procinfo stuff, we get that + later. */ + have = set_procinfo_flags (ps, need & ~have & test_msgport_flags, have); + + if (NEED (PSTAT_SUSPEND_COUNT, + ((have & PSTAT_PID) ? PSTAT_TASK_BASIC : PSTAT_THREAD_BASIC))) + { + if (have & PSTAT_PID) + ps->suspend_count = ps->task_basic_info->suspend_count; + else + ps->suspend_count = ps->thread_basic_info->suspend_count; + have |= PSTAT_SUSPEND_COUNT; + } + + ps->flags = have; /* should_suppress_msgport looks at them. */ + if (should_suppress_msgport (ps)) + suppress_msgport (); + + /* the process's struct procinfo as returned by proc_getprocinfo. */ + if (need & ~have & PSTAT_PROCINFO) + have = set_procinfo_flags (ps, need, have); + + /* The process's libc msg port (see <hurd/msg.defs>). */ + MGET(PSTAT_MSGPORT, PSTAT_PID, proc_getmsgport (server, ps->pid, &ps->msgport)); + /* The process's process port. */ + MGET(PSTAT_PROCESS, PSTAT_PID, proc_pid2proc (server, ps->pid, &ps->process)); + /* The process's task port. */ + MGET(PSTAT_TASK, PSTAT_PID, proc_pid2task (server, ps->pid, &ps->task)); + + /* VM statistics for the task. See <mach/task_info.h>. */ + if (NEED (PSTAT_TASK_EVENTS, PSTAT_TASK)) + { + ps->task_events_info = &ps->task_events_info_buf; + ps->task_events_info_size = TASK_EVENTS_INFO_COUNT; + if (task_info (ps->task, TASK_EVENTS_INFO, + (task_info_t)ps->task_events_info, + &ps->task_events_info_size) + == 0) + have |= PSTAT_TASK_EVENTS; + } + + /* PSTAT_STATE_ bits for the process and all its threads. */ + if ((need & PSTAT_STATE) && (have & (PSTAT_PROC_INFO | PSTAT_THREAD_BASIC))) + { + ps->state = 0; + + if (have & PSTAT_THREAD_BASIC) + { + /* Thread states. */ + if (have & PSTAT_THREAD) + ps->state |= thread_state (ps->thread_basic_info); + else + /* For a process, we use the thread list instead of + PS->thread_basic_info because it contains more information. */ + ps->state |= summarize_thread_states (ps->proc_info); + } + + if (have & PSTAT_PROC_INFO) + /* Process state. */ + { + int pi_flags = ps->proc_info->state; + if (pi_flags & PI_STOPPED) + ps->state |= PSTAT_STATE_P_STOP; + if (pi_flags & PI_ZOMBIE) + ps->state |= PSTAT_STATE_P_ZOMBIE; + if (pi_flags & PI_SESSLD) + ps->state |= PSTAT_STATE_P_SESSLDR; + if (pi_flags & PI_LOGINLD) + ps->state |= PSTAT_STATE_P_LOGINLDR; + if (!(pi_flags & PI_EXECED)) + ps->state |= PSTAT_STATE_P_FORKED; + if (pi_flags & PI_NOMSG) + ps->state |= PSTAT_STATE_P_NOMSG; + if (pi_flags & PI_NOPARENT) + ps->state |= PSTAT_STATE_P_NOPARENT; + if (pi_flags & PI_ORPHAN) + ps->state |= PSTAT_STATE_P_ORPHAN; + if (pi_flags & PI_TRACED) + ps->state |= PSTAT_STATE_P_TRACE; + if (pi_flags & PI_WAITING) + ps->state |= PSTAT_STATE_P_WAIT; + if (pi_flags & PI_GETMSG) + ps->state |= PSTAT_STATE_P_GETMSG; + } + + have |= PSTAT_STATE; + } + + /* The process's exec arguments */ + if (NEED (PSTAT_ARGS, PSTAT_PID)) + { + char *buf = malloc (100); + ps->args_len = 100; + ps->args = buf; + if (ps->args) + { + if (proc_getprocargs (server, ps->pid, &ps->args, &ps->args_len)) + free (buf); + else + { + have |= PSTAT_ARGS; + ps->args_vm_alloced = (ps->args != buf); + if (ps->args_vm_alloced) + free (buf); + } + } + } + + /* The process's exec environment */ + if (NEED (PSTAT_ENV, PSTAT_PID)) + { + char *buf = malloc (100); + ps->env_len = 100; + ps->env = buf; + if (ps->env) + { + if (proc_getprocenv (server, ps->pid, &ps->env, &ps->env_len)) + free (buf); + else + { + have |= PSTAT_ENV; + ps->env_vm_alloced = (ps->env != buf); + if (ps->env_vm_alloced) + free (buf); + } + } + } + + /* The ctty id port; note that this is just a magic cookie; + we use it to fetch a port to the actual terminal -- it's not useful for + much else. */ + MP_MGET (PSTAT_CTTYID, PSTAT_TASK, + ps_msg_get_init_port (ps->msgport, ps->task, + INIT_PORT_CTTYID, &ps->cttyid)); + + /* A port to the process's current working directory. */ + MP_MGET (PSTAT_CWDIR, PSTAT_TASK, + ps_msg_get_init_port (ps->msgport, ps->task, + INIT_PORT_CWDIR, &ps->cwdir)); + + /* The process's auth port, which we can use to determine who the process + is authenticated as. */ + MP_MGET (PSTAT_AUTH, PSTAT_TASK, + ps_msg_get_init_port (ps->msgport, ps->task, INIT_PORT_AUTH, + &ps->auth)); + + /* The process's umask, which controls which protection bits won't be set + when creating a file. */ + MP_MGET (PSTAT_UMASK, PSTAT_TASK, + ps_msg_get_init_int (ps->msgport, ps->task, INIT_UMASK, + &ps->umask)); + + if (NEED (PSTAT_OWNER_UID, PSTAT_PROC_INFO)) + { + if (ps->proc_info->state & PI_NOTOWNED) + ps->owner_uid = -1; + else + ps->owner_uid = ps->proc_info->owner; + have |= PSTAT_OWNER_UID; + } + + /* A ps_user object for the process's owner. */ + if (NEED (PSTAT_OWNER, PSTAT_OWNER_UID)) + { + if (ps->owner_uid < 0) + { + ps->owner = 0; + have |= PSTAT_OWNER; + } + else if (! ps_context_find_user (ps->context, ps->owner_uid, &ps->owner)) + have |= PSTAT_OWNER; + } + + /* A ps_tty for the process's controlling terminal, or NULL if it + doesn't have one. */ + if (NEED (PSTAT_TTY, PSTAT_CTTYID)) + if (ps_context_find_tty_by_cttyid (ps->context, ps->cttyid, &ps->tty) == 0) + have |= PSTAT_TTY; + + /* Update PS's flag state. We haven't tried user flags yet, so don't mark + them as having failed. We do this before checking user bits so that the + user fetch hook sees PS in a consistent state. */ + ps->failed |= (need & ~PSTAT_USER_MASK) & ~have; + ps->flags = have; + + need &= ~have; + if (need && ps->context->user_hooks && ps->context->user_hooks->fetch) + /* There is some user state we need to fetch. */ + { + have |= (*ps->context->user_hooks->fetch) (ps, need, have); + /* Update the flag state again having tried the user bits. We allow + the user hook to turn on non-user bits, in which case we remove them + from the failed set; the user hook may know some way of getting the + info that we don't. */ + ps->failed = (ps->failed | need) & ~have; + ps->flags = have; + } + + return 0; +} + +/* ---------------------------------------------------------------- */ +/* Discard PS and any resources it holds. */ +void +_proc_stat_free (ps) + struct proc_stat *ps; +{ + if (ps->context->user_hooks && ps->context->user_hooks->cleanup) + /* Free any user state. */ + (*ps->context->user_hooks->cleanup) (ps); + + /* Free the mach port PORT if FLAG is set in PS's flags. */ +#define MFREEPORT(flag, port) \ + ((ps->flags & (flag)) \ + ? mach_port_deallocate(mach_task_self (), (ps->port)) : 0) + + /* If FLAG is set in PS's flags, then if VM_ALLOCED is zero, free the malloced + field MEM in PS; othrewise, vm_deallocate MEM, consisting of SIZE + elements of type ELTYPE, *unless* MEM == SBUF, which usually means + that MEM points to a static buffer somewhere instead of vm_alloc'd + memory. */ +#define MFREEMEM(flag, mem, size, vm_alloced, sbuf, eltype) \ + (((ps->flags & (flag)) && ps->mem != sbuf) \ + ? (vm_alloced ? (VMFREE(ps->mem, size * sizeof (eltype))) : free (ps->mem)) : 0) + + /* free PS's ports */ + MFREEPORT (PSTAT_PROCESS, process); + MFREEPORT (PSTAT_TASK, task); + MFREEPORT (PSTAT_MSGPORT, msgport); + MFREEPORT (PSTAT_CTTYID, cttyid); + MFREEPORT (PSTAT_CWDIR, cwdir); + MFREEPORT (PSTAT_AUTH, auth); + + /* free any allocated memory pointed to by PS */ + MFREEMEM (PSTAT_PROCINFO, proc_info, ps->proc_info_size, + ps->proc_info_vm_alloced, 0, char); + MFREEMEM (PSTAT_THREAD_BASIC, thread_basic_info, 0, 0, 0, 0); + MFREEMEM (PSTAT_THREAD_SCHED, thread_sched_info, 0, 0, 0, 0); + MFREEMEM (PSTAT_ARGS, args, ps->args_len, ps->args_vm_alloced, 0, char); + MFREEMEM (PSTAT_ENV, env, ps->env_len, ps->env_vm_alloced, 0, char); + MFREEMEM (PSTAT_TASK_EVENTS, task_events_info, ps->task_events_info_size, + 0, &ps->task_events_info_buf, char); + + FREE (ps); +} + +/* Returns in PS a new proc_stat for the process PID at the process context + CONTEXT. If a memory allocation error occurs, ENOMEM is returned, + otherwise 0. */ +error_t +_proc_stat_create (pid_t pid, struct ps_context *context, struct proc_stat **ps) +{ + *ps = NEW (struct proc_stat); + if (*ps == NULL) + return ENOMEM; + + (*ps)->pid = pid; + (*ps)->flags = PSTAT_PID; + (*ps)->failed = 0; + (*ps)->inapp = PSTAT_THREAD; + (*ps)->context = context; + (*ps)->hook = 0; + + return 0; +} + +/* ---------------------------------------------------------------- */ + +/* Returns in THREAD_PS a proc_stat for the Nth thread in the proc_stat + PS (N should be between 0 and the number of threads in the process). The + resulting proc_stat isn't fully functional -- most flags can't be set in + it. It also contains a pointer to PS, so PS shouldn't be freed without + also freeing THREAD_PS. If N was out of range, EINVAL is returned. If a + memory allocation error occured, ENOMEM is returned. Otherwise, 0 is + returned. */ +error_t +proc_stat_thread_create (struct proc_stat *ps, unsigned index, struct proc_stat **thread_ps) +{ + error_t err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); + + if (err) + return err; + else if (index >= ps->num_threads) + return EINVAL; + else + { + struct proc_stat *tps = NEW (struct proc_stat); + + if (tps == NULL) + return ENOMEM; + + /* A value of -1 for the PID indicates that this is a thread. */ + tps->pid = -1; + tps->flags = PSTAT_THREAD; + tps->failed = 0; + tps->inapp = PSTAT_PID; + + tps->thread_origin = ps; + tps->thread_index = index; + + tps->context = ps->context; + + *thread_ps = tps; + + return 0; + } +} diff --git a/libps/ps.h b/libps/ps.h new file mode 100644 index 00000000..417ad374 --- /dev/null +++ b/libps/ps.h @@ -0,0 +1,1047 @@ +/* Routines to gather and print process information. + + Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __PS_H__ +#define __PS_H__ + +#include <hurd/hurd_types.h> +#include <hurd/ihash.h> +#include <mach/mach.h> + +#include <pwd.h> +#include <errno.h> + +/* A PS_USER holds info about a particular user. */ + +/* Possible states a ps_user's passwd can be in: valid, not fet */ +enum ps_user_passwd_state + { PS_USER_PASSWD_OK, PS_USER_PASSWD_PENDING, PS_USER_PASSWD_ERROR }; + +struct ps_user +{ + /* Which user this refers to. */ + uid_t uid; + + /* The status */ + enum ps_user_passwd_state passwd_state; + + /* The user's password file entry. Only valid if PASSWD_STATE == + PS_USER_PASSWD_OK. */ + struct passwd passwd; + + /* String storage for strings pointed to by ENTRY. */ + char *storage; +}; + +#define ps_user_uid(u) ((u)->uid) + +/* Create a ps_user for the user referred to by UID, returning it in U. + If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t ps_user_create (uid_t uid, struct ps_user **u); + +/* Create a ps_user for the user referred to by UNAME, returning it in U. + If a memory allocation error occurs, ENOMEM is returned. If no such user + is known, EINVAL is returned. */ +error_t ps_user_uname_create (char *uname, struct ps_user **u); + +/* Makes makes a ps_user containing PW (which is copied). */ +error_t ps_user_passwd_create (struct passwd *pw, struct ps_user **u); + +/* Free U and any resources it consumes. */ +void ps_user_free (struct ps_user *u); + +/* Returns the password file entry (struct passwd, from <pwd.h>) for the user + referred to by U, or NULL if it can't be gotten. */ +struct passwd *ps_user_passwd (struct ps_user *u); + +/* Returns the user name for the user referred to by U, or NULL if it can't + be gotten. */ +char *ps_user_name (struct ps_user *u); + +/* A ps_tty holds information about a terminal. */ + +/* Possible states a ps_tty's name can be in: valid, not fetched yet, + couldn't fetch. */ +enum ps_tty_name_state + { PS_TTY_NAME_OK, PS_TTY_NAME_PENDING, PS_TTY_NAME_ERROR }; + +struct ps_tty { + /* Which tty this refers to. */ + file_t port; + + /* The name of the tty, if we could figure it out. */ + const char *name; + /* What state the name is in. */ + enum ps_tty_name_state name_state; + + /* A more abbreviated name for the tty, or NULL if no name at all. */ + const char *short_name; + int short_name_alloced : 1; +}; + +#define ps_tty_port(tty) ((tty)->port) + +/* Create a ps_tty for the tty referred to by PORT, returning it in TTY. + If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t ps_tty_create (file_t port, struct ps_tty **tty); + +/* Frees TTY and any resources it consumes. */ +void ps_tty_free (struct ps_tty *tty); + +/* Returns the name of the tty, or NULL if it can't be figured out. */ +const char *ps_tty_name (struct ps_tty *tty); + +/* Returns the standard abbreviated name of the tty, the whole name if there + is no standard abbreviation, or NULL if it can't be figured out. */ +const char *ps_tty_short_name (struct ps_tty *tty); + +/* A PS_CONTEXT holds various information resulting from querying a + particular process server, in particular a group of proc_stats, ps_users, + and ps_ttys. This information sticks around until the context is freed + (subsets may be created by making proc_stat_lists). */ + +struct proc_stat; /* Fwd declared */ + +struct ps_context +{ + /* The process server our process info is from. */ + process_t server; + + /* proc_stats for every process we know about, indexed by process id. */ + ihash_t procs; + + /* ps_ttys for every tty we know about, indexed by the terminal port. */ + ihash_t ttys; + + /* ps_ttys for every tty we know about, indexed by their ctty id port + (from libc). */ + ihash_t ttys_by_cttyid; + + /* A ps_user for every user we know about, indexed by user-id. */ + ihash_t users; + + /* Functions that can be set to extend the behavior of proc_stats. */ + struct ps_user_hooks *user_hooks; +}; + +#define ps_context_server(pc) ((pc)->server) + +/* Returns in PC a new ps_context for the proc server SERVER. If a memory + allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t ps_context_create (process_t server, struct ps_context **pc); + +/* Frees PC and any resources it consumes. */ +void ps_context_free (struct ps_context *pc); + +/* Find a proc_stat for the process referred to by PID, and return it in + PS. If an error occurs, it is returned, otherwise 0. */ +error_t ps_context_find_proc_stat (struct ps_context *pc, pid_t pid, + struct proc_stat **ps); + +/* Find a ps_tty for the terminal referred to by the port TTY_PORT, and + return it in TTY. If an error occurs, it is returned, otherwise 0. */ +error_t ps_context_find_tty (struct ps_context *pc, mach_port_t tty_port, + struct ps_tty **tty); + +/* Find a ps_tty for the terminal referred to by the ctty id port + CTTYID_PORT, and return it in TTY. If an error occurs, it is returned, + otherwise 0. */ +error_t ps_context_find_tty_by_cttyid (struct ps_context *pc, + mach_port_t cttyid_port, + struct ps_tty **tty); + +/* Find a ps_user for the user referred to by UID, and return it in U. */ +error_t ps_context_find_user (struct ps_context *pc, uid_t uid, + struct ps_user **u); + +/* A PROC_STAT holds lots of info about the process PID at SERVER; exactly + which info is dependent on its FLAGS field. */ + +typedef unsigned ps_flags_t; +typedef unsigned ps_state_t; + +struct proc_stat +{ + /* Which process server this is from. */ + struct ps_context *context; + + /* The proc's process id; if <0 then this is a thread, not a process. */ + pid_t pid; + + /* Flags describing which fields in this structure are valid. */ + ps_flags_t flags; + ps_flags_t failed; /* flags that we tried to set and couldn't. */ + ps_flags_t inapp; /* flags that don't apply to this procstat; + subset of FAILED. */ + + /* Thread fields -- these are valid if PID < 0. */ + struct proc_stat *thread_origin; /* A proc_stat for the task we're in. */ + unsigned thread_index; /* Which thread in our proc we are. */ + + /* A process_t port for the process. */ + process_t process; + + /* The mach task port for the process. */ + task_t task; + + /* A libc msgport for the process. This port is responded to by the + process itself (usually by the c library); see <hurd/msg.defs> for the + standard set of rpcs you can send to this port. Accordingly, you + cannot depend on a timely (or any) reply to messages sent here -- + program carefully! */ + mach_port_t msgport; + + /* A pointer to the process's procinfo structure (as returned by + proc_getinfo; see <hurd/hurd_types.h>). Vm_alloced. */ + struct procinfo *proc_info; + /* The size of the info structure for deallocation purposes. */ + unsigned proc_info_size; + + /* If present, these are just pointers into the proc_info structure. */ + unsigned num_threads; + task_basic_info_t task_basic_info; + + /* For a thread, the obvious structures; for a process, summaries of the + proc's thread_{basic,sched}_info_t structures: sizes and cumulative + times are summed, prioritys and delta time are averaged. The + run_states are added by having running thread take precedence over + waiting ones, and if there are any other incompatible states, simply + using a bogus value of -1. Malloced. */ + thread_basic_info_t thread_basic_info; + thread_sched_info_t thread_sched_info; + + /* For a blocked thread, these next fields describe how it's blocked. */ + + /* A string (pointing into the thread_waits field of the parent + procstat), describing what's being blocked on. If "KERNEL", a system + call (not mach_msg), and thread_rpc is the system call number. + Otherwise if thread_rpc isn't zero, this string describes the port the + rpc is on; if thread_rpc is 0, this string describes a non-rpc event. */ + char *thread_wait; + /* The rpc that it's blocked on. For a process the rpc blocking the + first blocked thread (if any). 0 means no block. */ + mach_msg_id_t thread_rpc; + + /* Storage for child-thread waits. */ + char *thread_waits; + size_t thread_waits_len; + + /* The task or thread suspend count (whatever this proc_stat refers to). */ + int suspend_count; + + /* A bitmask summarizing the scheduling state of this process and all its + threads. See the PSTAT_STATE_ defines below for a list of bits. */ + ps_state_t state; + + /* A ps_user object for the owner of this process, or NULL if none. */ + struct ps_user *owner; + int owner_uid; /* The corresponding UID, or -1. */ + + /* The process's argv, as a string with each element separated by '\0'. */ + char *args; + /* The length of ARGS. */ + unsigned args_len; + + /* Virtual memory statistics for the process, as returned by task_info; + see <mach/task_info.h> for a description of task_events_info_t. */ + task_events_info_t task_events_info; + task_events_info_data_t task_events_info_buf; + unsigned task_events_info_size; + + /* Flags showing whether a field is vm_alloced or malloced. */ + unsigned proc_info_vm_alloced : 1; + unsigned thread_waits_vm_alloced : 1; + unsigned args_vm_alloced : 1; + unsigned env_vm_alloced : 1; + + /* Various libc ports: */ + + /* The process's ctty id port, or MACH_PORT_NULL if the process has no + controlling terminal. Note that this is just a magic cookie; we use + it to fetch a port to the actual terminal -- it's not useful for much + else. */ + mach_port_t cttyid; + + /* A port to the process's current working directory. */ + mach_port_t cwdir; + + /* The process's auth port, which we can use to determine who the process + is authenticated as. */ + mach_port_t auth; + + /* The process's umask, which controls which protection bits won't be set + when creating a file. */ + unsigned umask; + + /* A ps_tty object for the process's controlling terminal. */ + struct ps_tty *tty; + + /* A hook for the user to use. */ + void *hook; + + /* XXX these members added at the end for binary compatibility */ + /* The process's envp, as a string with each element separated by '\0'. */ + char *env; + /* The length of ENV. */ + unsigned env_len; +}; + +/* Proc_stat flag bits; each bit is set in the FLAGS field if that + information is currently valid. */ +#define PSTAT_PID 0x00001 /* Process ID */ +#define PSTAT_THREAD 0x00002 /* thread_index & thread_origin */ +#define PSTAT_PROCESS 0x00004 /* The process_t for the process */ +#define PSTAT_TASK 0x00008 /* The task port for the process */ +#define PSTAT_MSGPORT 0x00010 /* The process's msgport */ +#define PSTAT_PROC_INFO 0x00020 /* Basic process info. */ +#define PSTAT_TASK_BASIC 0x00040 /* The task's struct task_basic_info. */ +#define PSTAT_TASK_EVENTS 0x00080 /* A task_events_info_t for the proc. */ +#define PSTAT_NUM_THREADS 0x00100 /* The number of threads in the task. */ +/* Note that for a process-procstat, the thread information fields generally + are a summary of the process's threads, and imply that the corresponding + information has been fetched for all its threads. The exception is + thread-wait information (PSTAT_THREAD_WAIT), which is expensive to fetch + for processes with lots of threads, and not terrible useful. In this + case, the thread waits vector containing per-thread information is only + guaranteed to be valid if PSTAT_THREAD_WAITS is true as well. */ +#define PSTAT_THREAD_BASIC 0x00200 /* A struct thread_basic_info. */ +#define PSTAT_THREAD_SCHED 0x00400 /* A struct thread_sched_info. */ +#define PSTAT_THREAD_WAIT 0x00800 /* The rpc the thread is waiting on. */ +#define PSTAT_THREAD_WAITS 0x01000 /* Thread waits for this PS's threads */ +#define PSTAT_ARGS 0x02000 /* The process's args */ +#define PSTAT_ENV 0x2000000 /* The process's environment */ +#define PSTAT_STATE 0x04000 /* A bitmask describing the process's + state (see below) */ +#define PSTAT_SUSPEND_COUNT 0x08000 /* Task/thread suspend count */ +#define PSTAT_CTTYID 0x10000 /* The process's CTTYID port */ +#define PSTAT_CWDIR 0x20000 /* A file_t for the proc's CWD */ +#define PSTAT_AUTH 0x40000 /* The proc's auth port */ +#define PSTAT_TTY 0x80000 /* A ps_tty for the proc's terminal.*/ +#define PSTAT_OWNER 0x100000 /* A ps_user for the proc's owner */ +#define PSTAT_OWNER_UID 0x200000 /* The uid of the the proc's owner */ +#define PSTAT_UMASK 0x400000 /* The proc's current umask */ +#define PSTAT_HOOK 0x800000 /* Has a non-zero hook */ + +/* Flag bits that don't correspond precisely to any field. */ +#define PSTAT_NO_MSGPORT 0x1000000 /* Don't use the msgport at all */ + +/* Bits from PSTAT_USER_BASE on up are available for user-use. */ +#define PSTAT_USER_BASE 0x10000000 +#define PSTAT_USER_MASK ~(PSTAT_USER_BASE - 1) + +/* If the PSTAT_STATE flag is set, then the proc_stats state field holds a + bitmask of the following bits, describing the process's run state. If you + change the value of these, you must change proc_stat_state_tags as well! */ + +/* Process global state. */ + +/* Mutually exclusive bits, each of which is a possible process `state'. */ +#define PSTAT_STATE_P_STOP 0x00001 /* T stopped (e.g., by ^Z) */ +#define PSTAT_STATE_P_ZOMBIE 0x00002 /* Z process exited but not reaped */ + +#define PSTAT_STATE_P_STATES (PSTAT_STATE_P_STOP | PSTAT_STATE_P_ZOMBIE) + +/* Independent bits describing additional attributes of the process. */ +#define PSTAT_STATE_P_FG 0x00400 /* + in foreground process group */ +#define PSTAT_STATE_P_SESSLDR 0x00800 /* s session leader */ +#define PSTAT_STATE_P_LOGINLDR 0x01000 /* l login collection leader */ +#define PSTAT_STATE_P_FORKED 0x02000 /* f has forked and not execed */ +#define PSTAT_STATE_P_NOMSG 0x04000 /* m no msg port */ +#define PSTAT_STATE_P_NOPARENT 0x08000 /* p no parent */ +#define PSTAT_STATE_P_ORPHAN 0x10000 /* o orphaned */ +#define PSTAT_STATE_P_TRACE 0x20000 /* x traced */ +#define PSTAT_STATE_P_WAIT 0x40000 /* w process waiting for a child */ +#define PSTAT_STATE_P_GETMSG 0x80000 /* g waiting for a msgport */ + +#define PSTAT_STATE_P_ATTRS (PSTAT_STATE_P_FG | PSTAT_STATE_P_SESSLDR \ + | PSTAT_STATE_P_LOGINLDR | PSTAT_STATE_P_FORKED \ + | PSTAT_STATE_P_NOMSG | PSTAT_STATE_P_NOPARENT \ + | PSTAT_STATE_P_ORPHAN | PSTAT_STATE_P_TRACE \ + | PSTAT_STATE_P_WAIT | PSTAT_STATE_P_GETMSG) + +/* Per-thread state; in a process, these represent the union of its threads. */ + +/* Mutually exclusive bits, each of which is a possible thread `state'. */ +#define PSTAT_STATE_T_RUN 0x00004 /* R thread is running */ +#define PSTAT_STATE_T_HALT 0x00008 /* H thread is halted */ +#define PSTAT_STATE_T_WAIT 0x00010 /* D uninterruptable wait */ +#define PSTAT_STATE_T_SLEEP 0x00020 /* S sleeping */ +#define PSTAT_STATE_T_IDLE 0x00040 /* I idle (sleeping > 20 seconds) */ + +#define PSTAT_STATE_T_STATES (PSTAT_STATE_T_RUN | PSTAT_STATE_T_HALT \ + | PSTAT_STATE_T_WAIT | PSTAT_STATE_T_SLEEP \ + | PSTAT_STATE_T_IDLE) + +/* Independent bits describing additional attributes of the thread. */ +#define PSTAT_STATE_T_NICE 0x00080 /* N lowered priority */ +#define PSTAT_STATE_T_NASTY 0x00100 /* < raised priority */ +#define PSTAT_STATE_T_UNCLEAN 0x00200 /* u thread is uncleanly halted */ + +#define PSTAT_STATE_T_ATTRS (PSTAT_STATE_T_UNCLEAN \ + | PSTAT_STATE_T_NICE | PSTAT_STATE_T_NASTY) + +/* This is a constant string holding a single character for each possible bit + in a proc_stats STATE field, in order from bit zero. These are intended + for printing a user-readable summary of a process's state. */ +char *proc_stat_state_tags; + +/* Process info accessor functions. + + You must be sure that the associated flag bit is set before accessing a + field in a proc_stat! A field FOO (with accessor macro proc_foo ()), has + a flag named PSTAT_FOO. If the flag is'nt set, you may attempt to set it + with proc_stat_set_flags (but note that this may not succeed). */ + +/* FLAGS doesn't have a flag bit; it's always valid */ +#define proc_stat_flags(ps) ((ps)->flags) + +/* These both use the flag PSTAT_THREAD. */ +#define proc_stat_thread_origin(ps) ((ps)->thread_origin) +#define proc_stat_thread_index(ps) ((ps)->thread_index) + +#define proc_stat_pid(ps) ((ps)->pid) +#define proc_stat_process(ps) ((ps)->process) +#define proc_stat_task(ps) ((ps)->task) +#define proc_stat_msgport(ps) ((ps)->msgport) +#define proc_stat_proc_info(ps) ((ps)->proc_info) +#define proc_stat_num_threads(ps) ((ps)->num_threads) +#define proc_stat_task_basic_info(ps) ((ps)->task_basic_info) +#define proc_stat_thread_basic_info(ps) ((ps)->thread_basic_info) +#define proc_stat_thread_sched_info(ps) ((ps)->thread_sched_info) +#define proc_stat_thread_rpc(ps) ((ps)->thread_rpc) +#define proc_stat_thread_wait(ps) ((ps)->thread_rpc) +#define proc_stat_suspend_count(ps) ((ps)->suspend_count) +#define proc_stat_args(ps) ((ps)->args) +#define proc_stat_args_len(ps) ((ps)->args_len) +#define proc_stat_env(ps) ((ps)->env) +#define proc_stat_env_len(ps) ((ps)->env_len) +#define proc_stat_state(ps) ((ps)->state) +#define proc_stat_cttyid(ps) ((ps)->cttyid) +#define proc_stat_cwdir(ps) ((ps)->cwdir) +#define proc_stat_owner(ps) ((ps)->owner) +#define proc_stat_owner_uid(ps) ((ps)->owner_uid) +#define proc_stat_auth(ps) ((ps)->auth) +#define proc_stat_umask(ps) ((ps)->umask) +#define proc_stat_tty(ps) ((ps)->tty) +#define proc_stat_task_events_info(ps) ((ps)->task_events_info) +#define proc_stat_has(ps, needs) (((ps)->flags & needs) == needs) + +/* True if PS refers to a thread and not a process. */ +#define proc_stat_is_thread(ps) ((ps)->pid < 0) + +/* Returns in PS a new proc_stat for the process PID in the ps context PC. + If a memory allocation error occurs, ENOMEM is returned, otherwise 0. + Users shouldn't use this routine, use pc_context_find_proc_stat instead. */ +error_t _proc_stat_create (pid_t pid, struct ps_context *context, + struct proc_stat **ps); + +/* Frees PS and any memory/ports it references. Users shouldn't use this + routine; proc_stats are normally freed only when their ps_context goes + away. */ +void _proc_stat_free (struct proc_stat *ps); + +/* Adds FLAGS to PS's flags, fetching information as necessary to validate + the corresponding fields in PS. Afterwards you must still check the flags + field before using new fields, as something might have failed. Returns + a system error code if a fatal error occurred, and 0 otherwise. */ +error_t proc_stat_set_flags (struct proc_stat *ps, ps_flags_t flags); + +/* Returns in THREAD_PS a proc_stat for the Nth thread in the proc_stat + PS (N should be between 0 and the number of threads in the process). The + resulting proc_stat isn't fully functional -- most flags can't be set in + it. If N was out of range, EINVAL is returned. If a memory allocation + error occured, ENOMEM is returned. Otherwise, 0 is returned. */ +error_t proc_stat_thread_create (struct proc_stat *ps, unsigned n, + struct proc_stat **thread_ps); + +/* A struct ps_user_hooks holds functions that allow the user to extend the + behavior of libps. */ + +struct ps_user_hooks +{ + /* Given a set of flags in the range defined by PSTAT_USER_MASK, should + return any other flags (user or system) which should be set as a + precondition to setting them. */ + ps_flags_t (*dependencies) (ps_flags_t flags); + + /* Try and fetch the information corresponding to NEED (which is in the + range defined by PSTAT_USER_MASK), and fill in the necessary fields in + PS (probably in a user defined structure pointed to by the hook field). + The user flags corresponding to what is successfully fetched should be + returned. HAVE are the flags defining whas is currently valid in PS. */ + ps_flags_t (*fetch) (struct proc_stat *ps, ps_flags_t need, ps_flags_t have); + + /* When a proc_stat goes away, this function is called on it. */ + void (*cleanup) (struct proc_stat *ps); +}; + +/* A PS_GETTER describes how to get a particular value from a PROC_STAT. + + To get a value from a proc_stat PS with a getter, you must make sure all + the pstat_flags returned by ps_getter_needs (GETTER) are set in PS, and + then call the function returned ps_getter_function (GETTER) with PS as the + first argument. + + The way the actual value is returned from this funciton is dependent on + the type of the value: + For ints and floats, the value is the return value. + For strings, you must pass in two extra arguments, a char **, which is + filled in with a pointer to the string, or NULL if the string is NULL, + and an int *, which is filled in with the length of the string. */ + +struct ps_getter + { + /* The getter's name */ + char *name; + + /* What proc_stat flags need to be set as a precondition to calling this + getter's function. */ + ps_flags_t needs; + + /* A function that will get the value; the protocol between this function + and its caller is type-dependent. */ + void (*fn) (); + }; + +/* Access macros: */ +#define ps_getter_name(g) ((g)->name) +#define ps_getter_needs(g) ((g)->needs) +#define ps_getter_function(g) ((g)->fn) + +/* A PS_FILTER_T describes how to select some subset of a PROC_STAT_LIST_T */ + +struct ps_filter + { + /* Name of this filter. */ + char *name; + + /* The flags that need to be set in each proc_stat in the list to + call the filter's predicate function; if these flags can't be set in a + particular proc_stat, the function is not called, and it isn't deleted + from the list. */ + ps_flags_t needs; + + /* A function that returns true if called on a proc_stat that the + filter accepts, or false if the filter rejects it. */ + int (*fn) (struct proc_stat *ps); + }; + +/* Access macros: */ +#define ps_filter_name(f) ((f)->name) +#define ps_filter_needs(f) ((f)->needs) +#define ps_filter_predicate(f) ((f)->fn) + +/* Some predefined filters. These are structures; you must use the & + operator to get a ps_filter from them */ + +/* A filter that retains only processes owned by getuid () */ +extern const struct ps_filter ps_own_filter; +/* A filter that retains only processes that aren't session or login leaders */ +extern const struct ps_filter ps_not_leader_filter; +/* A filter that retains only processes with a controlling terminal */ +extern const struct ps_filter ps_ctty_filter; +/* A filter that retains only `unorphaned' process. A process is unorphaned + if it's a session leader, or the process's process group is not orphaned */ +extern const struct ps_filter ps_unorphaned_filter; +/* A filter that retains only `parented' process. Typically only hurd + processes have parents. */ +extern const struct ps_filter ps_parent_filter; +/* A filter that retains only processes/threads that aren't totally dead. */ +extern const struct ps_filter ps_alive_filter; + +/* A ps_stream describes an output stream for libps to use. */ + +struct ps_stream +{ + FILE *stream; /* The actual destination. */ + int pos; /* The number of characters output. */ + int spaces; /* The number of spaces pending. */ +}; + +/* Create a stream outputing to DEST, and return it in STREAM, or an error. */ +error_t ps_stream_create (FILE *dest, struct ps_stream **stream); + +/* Frees STREAM. The destination file is *not* closed. */ +void ps_stream_free (struct ps_stream *stream); + +/* Write at most MAX_LEN characters of STRING to STREAM (if MAX_LEN > the + length of STRING, then write all of it; if MAX_LEN == -1, then write all + of STRING regardless). */ +error_t ps_stream_write (struct ps_stream *stream, + const char *string, int max_len); + +/* Write NUM spaces to STREAM. NUM may be negative, in which case the same + number of adjacent spaces (written by other calls to ps_stream_space) are + consumed if possible. If an error occurs, the error code is returned, + otherwise 0. */ +error_t ps_stream_space (struct ps_stream *stream, int num); + +/* Write as many spaces to STREAM as required to make a field of width SOFAR + be at least WIDTH characters wide (the absolute value of WIDTH is used). + If an error occurs, the error code is returned, otherwise 0. */ +error_t ps_stream_pad (struct ps_stream *stream, int sofar, int width); + +/* Write a newline to STREAM, resetting its position to zero. */ +error_t ps_stream_newline (struct ps_stream *stream); + +/* Write the string BUF to STREAM, padded on one side with spaces to be at + least the absolute value of WIDTH long: if WIDTH >= 0, then on the left + side, otherwise on the right side. If an error occurs, the error code is + returned, otherwise 0. */ +error_t ps_stream_write_field (struct ps_stream *stream, + const char *buf, int width); + +/* Like ps_stream_write_field, but truncates BUF to make it fit into WIDTH. */ +error_t ps_stream_write_trunc_field (struct ps_stream *stream, + const char *buf, int width); + +/* Write the decimal representation of VALUE to STREAM, padded on one side + with spaces to be at least the absolute value of WIDTH long: if WIDTH >= + 0, then on the left side, otherwise on the right side. If an error + occurs, the error code is returned, otherwise 0. */ +error_t ps_stream_write_int_field (struct ps_stream *stream, + int value, int width); + +/* A PS_FMT_SPEC describes how to output something from a PROC_STAT; it + is a combination of a getter (describing how to get the value), an output + function (which outputs the result of the getter), and a compare function + (which can be used to sort proc_stats according to how they are + output). It also specifies the default width of the field in which the + output should be printed. */ + +struct ps_fmt_field; /* fwd decl */ + +struct ps_fmt_spec + { + /* The name of the spec (and it's title, if TITLE is NULL). */ + const char *name; + + /* The title to be printed in the headers. */ + const char *title; + + /* The width of the field that this spec will be printed in if not + overridden. */ + int width; + + /* A default value for the fields `precision'. */ + int precision; + + /* Default values for PS_FMT_FIELD_ flags. */ + int flags; + + const struct ps_getter *getter; + + /* A function that outputs what FIELD specifies in PS to STREAM. */ + error_t (*output_fn)(struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream); + + /* A function that, given two pses and a getter, will compare what + the getter gets for each ps, and return an integer ala qsort. This + may be NULL, in which case values in this field cannot be compared. */ + int (*cmp_fn)(struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter); + + /* A function that, given a ps and a getter, will return true if what the + getter gets from the ps is `nominal' -- a default unexciting value. + This may be NULL, in which case values in this field are _always_ + exciting... */ + int (*nominal_fn)(struct proc_stat *ps, const struct ps_getter *getter); + }; + +/* Accessor macros: */ +#define ps_fmt_spec_name(spec) ((spec)->name) +#define ps_fmt_spec_title(spec) ((spec)->title) +#define ps_fmt_spec_width(spec) ((spec)->width) +#define ps_fmt_spec_output_fn(spec) ((spec)->output_fn) +#define ps_fmt_spec_compare_fn(spec) ((spec)->cmp_fn) +#define ps_fmt_spec_nominal_fn(spec) ((spec)->nominal_fn) +#define ps_fmt_spec_getter(spec) ((spec)->getter) + +/* Returns true if a pointer into an array of struct ps_fmt_specs is at the + end. */ +#define ps_fmt_spec_is_end(spec) ((spec)->name == NULL) + +struct ps_fmt_specs +{ + const struct ps_fmt_spec *specs; /* An array of specs. */ + struct ps_fmt_specs *parent; /* A link to more specs shadowed by us. */ + struct ps_fmt_spec_block *expansions; /* Storage for expanded aliases. */ +}; + +/* An struct ps_fmt_specs, suitable for use with ps_fmt_specs_find, + containing specs for most values in a proc_stat. */ +extern struct ps_fmt_specs ps_std_fmt_specs; + +/* Searches for a spec called NAME in SPECS and returns it if found, + otherwise NULL. */ +const struct ps_fmt_spec *ps_fmt_specs_find (struct ps_fmt_specs *specs, + const char *name); + +/* A PS_FMT describes how to output user-readable version of a proc_stat. + It consists of a series of PS_FMT_FIELD_Ts, each describing how to output + one value. */ + +/* Flags for ps_fmt_fields. */ +#define PS_FMT_FIELD_AT_MOD 0x1 /* `@' modifier */ +#define PS_FMT_FIELD_COLON_MOD 0x2 /* `:' modifier */ +#define PS_FMT_FIELD_KEEP 0x4 /* Never nominalize this field. */ +#define PS_FMT_FIELD_UPCASE_TITLE 0x8 /* Upcase this field's title. */ + +/* PS_FMT_FIELD */ +struct ps_fmt_field + { + /* A ps_fmt_spec describing how to output this field's value, or NULL + if there is no value (in which case this is the last field, and exists + just to output its prefix string). */ + const struct ps_fmt_spec *spec; + + /* A non-zero-terminated string of characters that should be output + between the previous field and this one. */ + const char *pfx; + /* The number of characters from PFX that should be output. */ + unsigned pfx_len; + + /* The number of characters that the value portion of this field should + consume. If this field is negative, then the absolute value is used, + and the field should be right-aligned, otherwise, it is left-aligned. */ + int width; + + /* User-specifiable attributes, interpreted by each output format. */ + int precision; /* fraction following field width */ + + /* Flags, from the set PS_FMT_FIELD_. */ + int flags; + + /* Returns the title used when printing a header line for this field. */ + const char *title; + }; + +/* Accessor macros: */ +#define ps_fmt_field_fmt_spec(field) ((field)->spec) +#define ps_fmt_field_prefix(field) ((field)->pfx) +#define ps_fmt_field_prefix_length(field) ((field)->pfx_len) +#define ps_fmt_field_width(field) ((field)->width) +#define ps_fmt_field_title(field) ((field)->title) + +/* PS_FMT */ +struct ps_fmt +{ + /* A pointer to an array of struct ps_fmt_fields holding the individual + fields to be formatted. */ + struct ps_fmt_field *fields; + /* The (valid) length of the fields array. */ + unsigned num_fields; + + /* A set of proc_stat flags describing what a proc_stat needs to hold in + order to print out every field in the fmt. */ + ps_flags_t needs; + + /* Storage for various strings pointed to by the fields. */ + char *src; + size_t src_len; /* Size of SRC. */ + + /* The string displayed by default for fields that aren't appropriate for + this procstat. */ + char *inapp; + + /* The string displayed by default for fields which are appropiate, but + couldn't be fetched due to some error. */ + char *error; +}; + +/* Accessor macros: */ +#define ps_fmt_fields(fmt) ((fmt)->fields) +#define ps_fmt_num_fields(fmt) ((fmt)->num_fields) +#define ps_fmt_needs(fmt) ((fmt)->needs) +#define ps_fmt_inval (fmt) ((fmt)->inval) + +/* Make a PS_FMT by parsing the string SRC, searching for any named + field specs in FMT_SPECS, and returning the result in FMT. If a memory + allocation error occurs, ENOMEM is returned. If SRC contains an unknown + field name, EINVAL is returned. Otherwise 0 is returned. + + If POSIX is true, a posix-style format string is parsed, otherwise + the syntax of SRC is: + + SRC: FIELD* [ SUFFIX ] + FIELD: [ PREFIX ] SPEC + SPEC: `%' [ FLAGS ] [ `-' ] [ WIDTH ] [ `.' PRECISION ] NAMESPEC + FLAGS: `[!?@:]+' + WIDTH, PRECISION: `[0-9]+' + NAMESPEC: `{' NAME [ `:' TITLE ] `}' | NAME_AN + NAME, TITLE: `[^}]*' + NAME_AN: `[A-Za-z0-9_]*' + + PREFIXes and SUFFIXes are printed verbatim, and specs are replaced by the + output of the named spec with that name (each spec specifies what + proc_stat field to print, and how to print it, as well as a default field + width into which put the output). WIDTH is used to override the spec's + default width. If a `-' is included, the output is right-aligned within + this width, otherwise it is left-aligned. The FLAGS `@' & `:' are + spec-specific, `!' means never omit a nominal field, and `?' means omit a + field if it's nominal (in case the defualt is to never do so). PRECISION + has a spec-specific meaning. */ +error_t ps_fmt_create (char *src, int posix, struct ps_fmt_specs *fmt_specs, + struct ps_fmt **fmt); + +/* Given the same arguments as a previous call to ps_fmt_create that returned + an error, this function returns a malloced string describing the error. */ +void ps_fmt_creation_error (char *src, int posix, + struct ps_fmt_specs *fmt_specs, + char **error); + +/* Free FMT, and any resources it consumes. */ +void ps_fmt_free (struct ps_fmt *fmt); + +/* Return a copy of FMT in COPY, or an error. This is useful if, for + instance, you would like squash a format without destroying the original. */ +error_t ps_fmt_clone (struct ps_fmt *fmt, struct ps_fmt **copy); + +/* Write an appropiate header line for FMT, containing the titles of all its + fields appropiately aligned with where the values would be printed, to + STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t ps_fmt_write_titles (struct ps_fmt *fmt, struct ps_stream *stream); + +/* Format a description as instructed by FMT, of the process described by PS + to STREAM (without a trailing newline). If count is non-NULL, the total + number number of characters output is added to the integer it points to. + If any fatal error occurs, the error code is returned, otherwise 0. */ +error_t ps_fmt_write_proc_stat (struct ps_fmt *fmt, struct proc_stat *ps, + struct ps_stream *stream); + +/* Remove those fields from FMT for which the function FN, when called on the + field, returns true. Appropiate inter-field characters are also removed: + those *following* deleted fields at the beginning of the fmt, and those + *preceeding* deleted fields *not* at the beginning. */ +void ps_fmt_squash (struct ps_fmt *fmt, int (*fn)(struct ps_fmt_field *field)); + +/* Remove those fields from FMT which would need the proc_stat flags FLAGS. + Appropiate inter-field characters are also removed: those *following* + deleted fields at the beginning of the fmt, and those *preceeding* deleted + fields *not* at the beginning. */ +void ps_fmt_squash_flags (struct ps_fmt *fmt, ps_flags_t flags); + +/* Try and restrict the number of output columns in FMT to WIDTH. */ +void ps_fmt_set_output_width (struct ps_fmt *fmt, int width); + +/* A PROC_STAT_LIST represents a list of proc_stats */ + +struct proc_stat_list + { + /* An array of proc_stats for the processes in this list. */ + struct proc_stat **proc_stats; + + /* The number of processes in the list. */ + unsigned num_procs; + + /* The actual allocated length of PROC_STATS (in case we want to add more + processes). */ + unsigned alloced; + + /* Returns the proc context that these processes are from. */ + struct ps_context *context; + }; + +/* Accessor macros: */ +#define proc_stat_list_num_procs(pp) ((pp)->num_procs) +#define proc_stat_list_context(pp) ((pp)->context) + +/* Creates a new proc_stat_list_t for processes from CONTEXT, which is + returned in PP, and returns 0, or else returns ENOMEM if there wasn't + enough memory. */ +error_t proc_stat_list_create (struct ps_context *context, + struct proc_stat_list **pp); + +/* Free PP, and any resources it consumes. */ +void proc_stat_list_free (struct proc_stat_list *pp); + +/* Returns a copy of PP in COPY, or an error. */ +error_t proc_stat_list_clone (struct proc_stat_list *pp, + struct proc_stat_list **copy); + +/* Returns the proc_stat in PP with a process-id of PID, if there's one, + otherwise, NULL. */ +struct proc_stat *proc_stat_list_pid_proc_stat (struct proc_stat_list *pp, + pid_t pid); + +/* Add proc_stat entries to PP for each process with a process id in the + array PIDS (where NUM_PROCS is the length of PIDS). Entries are only + added for processes not already in PP. ENOMEM is returned if a memory + allocation error occurs, otherwise 0. PIDs is not referenced by the + resulting proc_stat_list_t, and so may be subsequently freed. If + PROC_STATS is non-NULL, a malloced array NUM_PROCS entries long of the + resulting proc_stats is returned in it. */ +error_t proc_stat_list_add_pids (struct proc_stat_list *pp, + pid_t *pids, unsigned num_procs, + struct proc_stat ***proc_stats); + +/* Add a proc_stat for the process designated by PID at PP's proc context + to PP. If PID already has an entry in PP, nothing is done. If a memory + allocation error occurs, ENOMEM is returned, otherwise 0. If PS is + non-NULL, the resulting entry is returned in it. */ +error_t proc_stat_list_add_pid (struct proc_stat_list *pp, pid_t pid, + struct proc_stat **ps); + +/* Adds all proc_stats in MERGEE to PP that don't correspond to processes + already in PP; the resulting order of proc_stats in PP is undefined. + If MERGEE and PP point to different proc contexts, EINVAL is returned. If a + memory allocation error occurs, ENOMEM is returned. Otherwise 0 is + returned, and MERGEE is freed. */ +error_t proc_stat_list_merge (struct proc_stat_list *pp, + struct proc_stat_list *mergee); + +/* Add to PP entries for all processes at its context. If an error occurs, + the system error code is returned, otherwise 0. If PROC_STATS and + NUM_PROCS are non-NULL, a malloced vector of the resulting entries is + returned in them. */ +error_t proc_stat_list_add_all (struct proc_stat_list *pp, + struct proc_stat ***proc_stats, + unsigned *num_procs); + +/* Add to PP entries for all processes in the login collection LOGIN_ID at + its context. If an error occurs, the system error code is returned, + otherwise 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector + of the resulting entries is returned in them. */ +error_t proc_stat_list_add_login_coll (struct proc_stat_list *pp, + pid_t login_id, + struct proc_stat ***proc_stats, + unsigned *num_procs); + +/* Add to PP entries for all processes in the session SESSION_ID at its + context. If an error occurs, the system error code is returned, otherwise + 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the + resulting entries is returned in them. */ +error_t proc_stat_list_add_session (struct proc_stat_list *pp, + pid_t session_id, + struct proc_stat ***proc_stats, + unsigned *num_procs); + +/* Add to PP entries for all processes in the process group PGRP at its + context. If an error occurs, the system error code is returned, otherwise + 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the + resulting entries is returned in them. */ +error_t proc_stat_list_add_pgrp (struct proc_stat_list *pp, pid_t pgrp, + struct proc_stat ***proc_stats, + unsigned *num_procs); + +/* Try to set FLAGS in each proc_stat in PP (but they may still not be set + -- you have to check). If a fatal error occurs, the error code is + returned, otherwise 0. */ +error_t proc_stat_list_set_flags (struct proc_stat_list *pp, ps_flags_t flags); + +/* Destructively modify PP to only include proc_stats for which the + function PREDICATE returns true; if INVERT is true, only proc_stats for + which PREDICATE returns false are kept. FLAGS is the set of pstat_flags + that PREDICATE requires be set as precondition. Regardless of the value + of INVERT, all proc_stats for which the predicate's preconditions can't + be satisfied are kept. If a fatal error occurs, the error code is + returned, it returns 0. */ +error_t proc_stat_list_filter1 (struct proc_stat_list *pp, + int (*predicate)(struct proc_stat *ps), + ps_flags_t flags, + int invert); + +/* Destructively modify PP to only include proc_stats for which the + predicate function in FILTER returns true; if INVERT is true, only + proc_stats for which the predicate returns false are kept. Regardless + of the value of INVERT, all proc_stats for which the predicate's + preconditions can't be satisfied are kept. If a fatal error occurs, + the error code is returned, it returns 0. */ +error_t proc_stat_list_filter (struct proc_stat_list *pp, + const struct ps_filter *filter, int invert); + +/* Destructively sort proc_stats in PP by ascending value of the field + returned by GETTER, and compared by CMP_FN; If REVERSE is true, use the + opposite order. If a fatal error occurs, the error code is returned, it + returns 0. */ +error_t proc_stat_list_sort1 (struct proc_stat_list *pp, + const struct ps_getter *getter, + int (*cmp_fn)(struct proc_stat *ps1, + struct proc_stat *ps2, + const struct ps_getter *getter), + int reverse); + +/* Destructively sort proc_stats in PP by ascending value of the field KEY; + if REVERSE is true, use the opposite order. If KEY isn't a valid sort + key, EINVAL is returned. If a fatal error occurs the error code is + returned. Otherwise, 0 is returned. */ +error_t proc_stat_list_sort (struct proc_stat_list *pp, + const struct ps_fmt_spec *key, int reverse); + +/* Format a description as instructed by FMT, of the processes in PP to + STREAM, separated by newlines (and with a terminating newline). If COUNT + is non-NULL, it points to an integer which is incremented by the number of + characters output. If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t proc_stat_list_fmt (struct proc_stat_list *pp, struct ps_fmt *fmt, + struct ps_stream *stream); + +/* Modifies FLAGS to be the subset which can't be set in any proc_stat in + PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat + as possible). If a fatal error occurs, the error code is returned, + otherwise 0. */ +error_t proc_stat_list_find_bogus_flags (struct proc_stat_list *pp, + ps_flags_t *flags); + +/* Add thread entries for for every process in PP, located immediately after + the containing process in sequence. Subsequent sorting of PP will leave + the thread entries located after the containing process, although the + order of the thread entries themselves may change. If a fatal error + occurs, the error code is returned, otherwise 0. */ +error_t proc_stat_list_add_threads (struct proc_stat_list *pp); + +error_t proc_stat_list_remove_threads (struct proc_stat_list *pp); + +/* Calls FN in order for each proc_stat in PP. If FN ever returns a non-zero + value, then the iteration is stopped, and the value is returned + immediately; otherwise, 0 is returned. */ +int proc_stat_list_for_each (struct proc_stat_list *pp, + int (*fn)(struct proc_stat *ps)); + +/* Returns true if SPEC is `nominal' in every entry in PP. */ +int proc_stat_list_spec_nominal (struct proc_stat_list *pp, + const struct ps_fmt_spec *spec); + +/* The Basic & Sched info types are pretty static, so we cache them, but load + info is dynamic so we don't cache that. See <mach/host_info.h> for + information on the data types these routines return. */ + +/* Return the current host port. */ +host_t ps_get_host (); + +/* Return a pointer to basic info about the current host in HOST_INFO. Since + this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_basic_info (host_basic_info_t *host_info); + +/* Return a pointer to scheduling info about the current host in HOST_INFO. + Since this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_sched_info (host_sched_info_t *host_info); + +/* Return a pointer to load info about the current host in HOST_INFO. Since + this is global information we just use a static buffer (if someone desires + to keep old load info, they should copy the buffer we return a pointer + to). If a system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_load_info (host_load_info_t *host_info); + +#endif /* __PS_H__ */ diff --git a/libps/pshost.h b/libps/pshost.h new file mode 100644 index 00000000..62b74876 --- /dev/null +++ b/libps/pshost.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 1995 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __PSHOST_H__ +#define __PSHOST_H__ + +#include <mach/mach_types.h> +#include <mach/host_info.h> + +/* ---------------------------------------------------------------- */ + +/* + The Basic & Sched info types are pretty static, so we cache them, but load + info is dynamic so we don't cache that. + + See <mach/host_info.h> for information on the data types these routines + return. +*/ + +/* Return the current host port. */ +host_t ps_get_host(); + +/* Return a pointer to basic info about the current host in HOST_INFO. Since + this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_basic_info(host_basic_info_t *host_info); + +/* Return a pointer to scheduling info about the current host in HOST_INFO. + Since this is static global information we just use a static buffer. If a + system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_sched_info(host_sched_info_t *host_info); + +/* Return a pointer to load info about the current host in HOST_INFO. Since + this is global information we just use a static buffer (if someone desires + to keep old load info, they should copy the buffer we return a pointer + to). If a system error occurs, the error code is returned, otherwise 0. */ +error_t ps_host_load_info(host_load_info_t *host_info); + +#endif /* __PSHOST_H__ */ diff --git a/libps/spec.c b/libps/spec.c new file mode 100644 index 00000000..1e30d95b --- /dev/null +++ b/libps/spec.c @@ -0,0 +1,1152 @@ +/* Access, formatting, & comparison routines for printing process info. + + Copyright (C) 1995,96,97,99 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <pwd.h> +#include <hurd/resource.h> +#include <unistd.h> +#include <string.h> +#include <timefmt.h> +#include <sys/time.h> + +#include "ps.h" +#include "common.h" + +/* XXX */ +static char *get_syscall_name (int num) { return 0; } +static char *get_rpc_name (mach_msg_id_t it) { return 0; } + +/* ---------------------------------------------------------------- */ +/* Getter definitions */ + +typedef void (*vf)(); + +static int +ps_get_pid (struct proc_stat *ps) +{ + return proc_stat_pid (ps); +} +const struct ps_getter ps_pid_getter = +{"pid", PSTAT_PID, (vf) ps_get_pid}; + +static int +ps_get_thread_index (struct proc_stat *ps) +{ + return proc_stat_thread_index (ps); +} +const struct ps_getter ps_thread_index_getter = +{"thread_index", PSTAT_THREAD, (vf) ps_get_thread_index}; + +static struct ps_user * +ps_get_owner (struct proc_stat *ps) +{ + return proc_stat_owner (ps); +} +const struct ps_getter ps_owner_getter = +{"owner", PSTAT_OWNER, (vf) ps_get_owner}; + +static int +ps_get_owner_uid (struct proc_stat *ps) +{ + return proc_stat_owner_uid (ps); +} +const struct ps_getter ps_owner_uid_getter = +{"uid", PSTAT_OWNER_UID, (vf) ps_get_owner_uid}; + +static int +ps_get_ppid (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->ppid; +} +const struct ps_getter ps_ppid_getter = +{"ppid", PSTAT_PROC_INFO, (vf) ps_get_ppid}; + +static int +ps_get_pgrp (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->pgrp; +} +const struct ps_getter ps_pgrp_getter = +{"pgrp", PSTAT_PROC_INFO, (vf) ps_get_pgrp}; + +static int +ps_get_session (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->session; +} +const struct ps_getter ps_session_getter = +{"session", PSTAT_PROC_INFO, (vf) ps_get_session}; + +static int +ps_get_login_col (struct proc_stat *ps) +{ + return proc_stat_proc_info (ps)->logincollection; +} +const struct ps_getter ps_login_col_getter = +{"login_col", PSTAT_PROC_INFO, (vf) ps_get_login_col}; + +static int +ps_get_num_threads (struct proc_stat *ps) +{ + return proc_stat_num_threads (ps); +} +const struct ps_getter ps_num_threads_getter = +{"num_threads", PSTAT_NUM_THREADS, (vf)ps_get_num_threads}; + +static void +ps_get_args (struct proc_stat *ps, char **args_p, int *args_len_p) +{ + *args_p = proc_stat_args (ps); + *args_len_p = proc_stat_args_len (ps); +} +const struct ps_getter ps_args_getter = +{"args", PSTAT_ARGS, ps_get_args}; + +static void +ps_get_env (struct proc_stat *ps, char **env_p, int *env_len_p) +{ + *env_p = proc_stat_env (ps); + *env_len_p = proc_stat_env_len (ps); +} +const struct ps_getter ps_env_getter = +{"env", PSTAT_ENV, ps_get_env}; + +static int +ps_get_state (struct proc_stat *ps) +{ + return proc_stat_state (ps); +} +const struct ps_getter ps_state_getter = +{"state", PSTAT_STATE, (vf) ps_get_state}; + +static void +ps_get_wait (struct proc_stat *ps, char **wait, int *rpc) +{ + *wait = ps->thread_wait; + *rpc = ps->thread_rpc; +} +const struct ps_getter ps_wait_getter = +{"wait", PSTAT_THREAD_WAIT, ps_get_wait}; + +static int +ps_get_vsize (struct proc_stat *ps) +{ + return proc_stat_task_basic_info (ps)->virtual_size; +} +const struct ps_getter ps_vsize_getter = +{"vsize", PSTAT_TASK_BASIC, (vf) ps_get_vsize}; + +static int +ps_get_rsize (struct proc_stat *ps) +{ + return proc_stat_task_basic_info (ps)->resident_size; +} +const struct ps_getter ps_rsize_getter = +{"rsize", PSTAT_TASK_BASIC, (vf) ps_get_rsize}; + +static int +ps_get_cur_priority (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->cur_priority; +} +const struct ps_getter ps_cur_priority_getter = +{"cur_priority", PSTAT_THREAD_BASIC, (vf) ps_get_cur_priority}; + +static int +ps_get_base_priority (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->base_priority; +} +const struct ps_getter ps_base_priority_getter = +{"base_priority", PSTAT_THREAD_BASIC, (vf) ps_get_base_priority}; + +static int +ps_get_max_priority (struct proc_stat *ps) +{ + return proc_stat_thread_sched_info (ps)->max_priority; +} +const struct ps_getter ps_max_priority_getter = +{"max_priority", PSTAT_THREAD_SCHED, (vf) ps_get_max_priority}; + +static void +ps_get_usr_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t tvt = proc_stat_thread_basic_info (ps)->user_time; + tv->tv_sec = tvt.seconds; + tv->tv_usec = tvt.microseconds; +} +const struct ps_getter ps_usr_time_getter = +{"usr_time", PSTAT_THREAD_BASIC, ps_get_usr_time}; + +static void +ps_get_sys_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t tvt = proc_stat_thread_basic_info (ps)->system_time; + tv->tv_sec = tvt.seconds; + tv->tv_usec = tvt.microseconds; +} +const struct ps_getter ps_sys_time_getter = +{"sys_time", PSTAT_THREAD_BASIC, ps_get_sys_time}; + +static void +ps_get_tot_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t tvt = proc_stat_thread_basic_info (ps)->user_time; + time_value_add (&tvt, &proc_stat_thread_basic_info (ps)->system_time); + tv->tv_sec = tvt.seconds; + tv->tv_usec = tvt.microseconds; +} +const struct ps_getter ps_tot_time_getter = +{"tot_time", PSTAT_THREAD_BASIC, ps_get_tot_time}; + +static void +ps_get_start_time (struct proc_stat *ps, struct timeval *tv) +{ + time_value_t *const tvt = &proc_stat_task_basic_info (ps)->creation_time; + tv->tv_sec = tvt->seconds; + tv->tv_usec = tvt->microseconds; +} +const struct ps_getter ps_start_time_getter = +{"start_time", PSTAT_TASK_BASIC, ps_get_start_time}; + +static float +ps_get_rmem_frac (struct proc_stat *ps) +{ + static int mem_size = 0; + + if (mem_size == 0) + { + host_basic_info_t info; + error_t err = ps_host_basic_info (&info); + if (err == 0) + mem_size = info->memory_size; + } + + if (mem_size > 0) + return + (float)proc_stat_task_basic_info (ps)->resident_size + / (float)mem_size; + else + return 0.0; +} +const struct ps_getter ps_rmem_frac_getter = +{"rmem_frac", PSTAT_TASK_BASIC, (vf) ps_get_rmem_frac}; + +static float +ps_get_cpu_frac (struct proc_stat *ps) +{ + return (float) proc_stat_thread_basic_info (ps)->cpu_usage + / (float) TH_USAGE_SCALE; +} +const struct ps_getter ps_cpu_frac_getter = +{"cpu_frac", PSTAT_THREAD_BASIC, (vf) ps_get_cpu_frac}; + +static int +ps_get_sleep (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->sleep_time; +} +const struct ps_getter ps_sleep_getter = +{"sleep", PSTAT_THREAD_BASIC, (vf) ps_get_sleep}; + +static int +ps_get_susp_count (struct proc_stat *ps) +{ + return proc_stat_suspend_count (ps); +} +const struct ps_getter ps_susp_count_getter = +{"susp_count", PSTAT_SUSPEND_COUNT, (vf) ps_get_susp_count}; + +static int +ps_get_proc_susp_count (struct proc_stat *ps) +{ + return proc_stat_task_basic_info (ps)->suspend_count; +} +const struct ps_getter ps_proc_susp_count_getter = +{"proc_susp_count", PSTAT_TASK_BASIC, (vf) ps_get_proc_susp_count}; + +static int +ps_get_thread_susp_count (struct proc_stat *ps) +{ + return proc_stat_thread_basic_info (ps)->suspend_count; +} +const struct ps_getter ps_thread_susp_count_getter = +{"thread_susp_count", PSTAT_SUSPEND_COUNT, (vf) ps_get_thread_susp_count}; + +static struct ps_tty * +ps_get_tty (struct proc_stat *ps) +{ + return proc_stat_tty (ps); +} +const struct ps_getter ps_tty_getter = +{"tty", PSTAT_TTY, (vf)ps_get_tty}; + +static int +ps_get_page_faults (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->faults; +} +const struct ps_getter ps_page_faults_getter = +{"page_faults", PSTAT_TASK_EVENTS, (vf) ps_get_page_faults}; + +static int +ps_get_cow_faults (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->cow_faults; +} +const struct ps_getter ps_cow_faults_getter = +{"cow_faults", PSTAT_TASK_EVENTS, (vf) ps_get_cow_faults}; + +static int +ps_get_pageins (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->pageins; +} +const struct ps_getter ps_pageins_getter = +{"pageins", PSTAT_TASK_EVENTS, (vf) ps_get_pageins}; + +static int +ps_get_msgs_sent (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->messages_sent; +} +const struct ps_getter ps_msgs_sent_getter = +{"msgs_sent", PSTAT_TASK_EVENTS, (vf) ps_get_msgs_sent}; + +static int +ps_get_msgs_rcvd (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->messages_received; +} +const struct ps_getter ps_msgs_rcvd_getter = +{"msgs_rcvd", PSTAT_TASK_EVENTS, (vf) ps_get_msgs_rcvd}; + +static int +ps_get_zero_fills (struct proc_stat *ps) +{ + return proc_stat_task_events_info (ps)->zero_fills; +} +const struct ps_getter ps_zero_fills_getter = +{"zero_fills", PSTAT_TASK_EVENTS, (vf) ps_get_zero_fills}; + +/* ---------------------------------------------------------------- */ +/* some printing functions */ + +/* G () is a helpful macro that just returns the getter G's access function + cast into a function pointer returning TYPE, as how the function should be + called varies depending on the getter. */ +#define G(getter,type) ((type (*)())((getter)->fn)) + +/* Similar to G, but takes a fmt field and uses its getter. */ +#define FG(field,type) G(field->spec->getter, type) + +error_t +ps_emit_int (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + return ps_stream_write_int_field (stream, FG (field, int)(ps), field->width); +} + +error_t +ps_emit_nz_int (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int value = FG (field, int)(ps); + if (value) + return ps_stream_write_int_field (stream, value, field->width); + else + return ps_stream_write_field (stream, "-", field->width); +} + +error_t +ps_emit_priority (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + return + ps_stream_write_int_field (stream, + MACH_PRIORITY_TO_NICE (FG (field, int)(ps)), + field->width); +} + +error_t +ps_emit_num_blocks (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + sprintf(buf, "%d", FG (field, int)(ps) / 1024); + return ps_stream_write_field (stream, buf, field->width); +} + +int +sprint_frac_value (char *buf, + int value, int min_value_len, + int frac, int frac_scale, + int width) +{ + int value_len; + int frac_len; + + if (value >= 100) /* the integer part */ + value_len = 3; + else if (value >= 10) + value_len = 2; + else + value_len = 1; + + while (value_len < min_value_len--) + *buf++ = '0'; + + for (frac_len = frac_scale + ; frac_len > 0 && (width < value_len + 1 + frac_len || frac % 10 == 0) + ; frac_len--) + frac /= 10; + + if (frac_len > 0) + sprintf (buf, "%d.%0*d", value, frac_len, frac); + else + sprintf (buf, "%d", value); + + return strlen (buf); +} + +error_t +ps_emit_percent (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + int width = field->width; + float perc = FG (field, float)(ps) * 100; + + if (width == 0) + sprintf (buf, "%g", perc); + else if (ABS (width) > 3) + sprintf(buf, "%.*f", ABS (width) - 3, perc); + else + sprintf (buf, "%d", (int) perc); + + return ps_stream_write_field (stream, buf, width); +} + +/* prints its value nicely */ +error_t +ps_emit_nice_int (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + int value = FG (field, int)(ps); + char *sfx = " KMG"; + int frac = 0; + + while (value >= 1024) + { + frac = ((value & 0x3FF) * 1000) >> 10; + value >>= 10; + sfx++; + } + + sprintf(buf + + sprint_frac_value (buf, value, 1, frac, 3, ABS (field->width) - 1), + "%c", *sfx); + + return ps_stream_write_field (stream, buf, field->width); +} + +error_t +ps_emit_seconds (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + struct timeval tv; + int width = field->width, prec = field->precision; + + FG (field, void)(ps, &tv); + + if ((field->flags & PS_FMT_FIELD_COLON_MOD) && tv.tv_sec == 0) + strcpy (buf, "-"); + else + fmt_seconds (&tv, !(field->flags & PS_FMT_FIELD_AT_MOD), prec, ABS (width), + buf, sizeof (buf)); + + return ps_stream_write_field (stream, buf, width); +} + +error_t +ps_emit_minutes (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char buf[20]; + struct timeval tv; + int width = field->width; + + FG (field, void)(ps, &tv); + + if ((field->flags & PS_FMT_FIELD_COLON_MOD) && tv.tv_sec < 60) + strcpy (buf, "-"); + else + fmt_minutes (&tv, !(field->flags & PS_FMT_FIELD_AT_MOD), ABS (width), + buf, sizeof (buf)); + + return ps_stream_write_field (stream, buf, width); +} + +error_t +ps_emit_past_time (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + static struct timeval now; + char buf[20]; + struct timeval tv; + int width = field->width; + + FG (field, void)(ps, &tv); + + if (now.tv_sec == 0 && gettimeofday (&now, 0) < 0) + return errno; + + fmt_past_time (&tv, &now, ABS (width), buf, sizeof buf); + + return ps_stream_write_field (stream, buf, width); +} + +error_t +ps_emit_uid (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int uid = FG (field, int)(ps); + if (uid < 0) + return ps_stream_write_field (stream, "-", field->width); + else + return ps_stream_write_int_field (stream, uid, field->width); +} + +error_t +ps_emit_uname (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int width = field->width; + struct ps_user *u = FG (field, struct ps_user *)(ps); + if (u) + { + struct passwd *pw = ps_user_passwd (u); + if (pw == NULL) + return ps_stream_write_int_field (stream, ps_user_uid (u), width); + else + return ps_stream_write_field (stream, pw->pw_name, width); + } + else + return ps_stream_write_field (stream, "-", width); +} + +error_t +ps_emit_user_name (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int width = field->width; + struct ps_user *u = FG (field, struct ps_user *)(ps); + if (u) + { + struct passwd *pw = ps_user_passwd (u); + if (pw == NULL) + { + char buf[20]; + sprintf (buf, "(UID %d)", pw->pw_uid); + return ps_stream_write_field (stream, buf, width); + } + else + return ps_stream_write_field (stream, pw->pw_gecos, width); + } + else + return ps_stream_write_field (stream, "-", width); +} + +/* prints a string with embedded nuls as spaces */ +error_t +ps_emit_args (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char *s0, *p, *q; + int s0len; + int width = field->width; + int fwidth = ABS (width); + char static_buf[200]; + char *buf = static_buf; + + FG (field, void)(ps, &s0, &s0len); + + if (!s0 || s0len == 0 ) + strcpy (buf, "-"); + else + { + if (s0len > sizeof static_buf) + { + buf = malloc (s0len + 1); + if (buf == NULL) + return ENOMEM; + } + + if (fwidth == 0 || fwidth > s0len) + fwidth = s0len; + + for (p = buf, q = s0; fwidth-- > 0; p++, q++) + { + int ch = *q; + *p = (ch == '\0' ? ' ' : ch); + } + if (q > s0 && *(q - 1) == '\0') + *--p = '\0'; + else + *p = '\0'; + } + + { + error_t err = ps_stream_write_trunc_field (stream, buf, width); + if (buf != static_buf) + free (buf); + return err; + } +} + +error_t +ps_emit_string (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char *str; + int len; + + FG (field, void)(ps, &str, &len); + + if (!str || len == 0) + str = "-"; + + return ps_stream_write_trunc_field (stream, str, field->width); +} + +error_t +ps_emit_tty_name (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + const char *name = "-"; + struct ps_tty *tty = FG (field, struct ps_tty *)(ps); + + if (tty) + { + name = ps_tty_short_name (tty); + if (name == NULL || *name == '\0') + name = "?"; + } + + return ps_stream_write_field (stream, name, field->width); +} + +struct state_shadow +{ + /* If any states in STATES are set, the states in shadow are suppressed. */ + int states; + int shadow; +}; + +static const struct state_shadow +state_shadows[] = { + /* If the process has no parent, it's not a hurd process, and various hurd + process bits are likely to be noise, so turn them off (but leave the + noparent bit on). */ + { PSTAT_STATE_P_NOPARENT, (PSTAT_STATE_P_ATTRS & ~PSTAT_STATE_P_NOPARENT) }, + /* Don't show sleeping thread if one is running, or the process is stopped.*/ + { PSTAT_STATE_T_RUN | PSTAT_STATE_P_STOP, + PSTAT_STATE_T_SLEEP | PSTAT_STATE_T_IDLE | PSTAT_STATE_T_WAIT }, + /* Only show the longest sleep. */ + { PSTAT_STATE_T_IDLE, PSTAT_STATE_T_SLEEP | PSTAT_STATE_T_WAIT }, + { PSTAT_STATE_T_SLEEP, PSTAT_STATE_T_WAIT }, + /* Turn off the thread stop bits if any thread is not stopped. This is + generally reasonable, as threads are often suspended to be frobed; if + they're all suspended, then something's odd (probably in the debugger, + or crashed). */ + { PSTAT_STATE_T_STATES & ~PSTAT_STATE_T_HALT, + PSTAT_STATE_T_HALT | PSTAT_STATE_T_UNCLEAN }, + { 0 } +}; + +error_t +ps_emit_state (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + char *tags; + int raw_state = FG (field, int)(ps); + int state = raw_state; + char buf[20], *p = buf; + const struct state_shadow *shadow = state_shadows; + + while (shadow->states) + { + if (raw_state & shadow->states) + state &= ~shadow->shadow; + shadow++; + } + + for (tags = proc_stat_state_tags + ; state != 0 && *tags != '\0' + ; state >>= 1, tags++) + if (state & 1) + *p++ = *tags; + + *p = '\0'; + + return ps_stream_write_field (stream, buf, field->width); +} + +error_t +ps_emit_wait (struct proc_stat *ps, struct ps_fmt_field *field, + struct ps_stream *stream) +{ + int rpc; + char *wait; + char buf[80]; + + FG (field, void)(ps, &wait, &rpc); + + if (wait == 0) + return ps_stream_write_field (stream, "?", field->width); + else if (*wait == 0) + return ps_stream_write_field (stream, "-", field->width); + else if (strcmp (wait, "kernel") == 0) + /* A syscall. RPC is actually the syscall number. */ + { + extern char *get_syscall_name (int num); + char *name = get_syscall_name (rpc); + if (! name) + { + sprintf (buf, "syscall:%d", -rpc); + name = buf; + } + return ps_stream_write_field (stream, name, field->width); + } + else if (rpc) + /* An rpc (with msg id RPC); WAIT describes the dest port. */ + { + char port_name_buf[20]; + extern char *get_rpc_name (mach_msg_id_t num); + char *name = get_rpc_name (rpc); + + /* See if we should give a more useful name for the port. */ + if (strcmp (wait, "init#0") == 0) + wait = "cwd"; /* Current directory */ + else if (strcmp (wait, "init#1") == 0) + wait = "root"; /* Root directory */ + else if (strcmp (wait, "init#2") == 0) + wait = "auth"; /* Auth port */ + else if (strcmp (wait, "init#3") == 0) + wait = "proc"; /* Proc port */ + else if (strcmp (wait, "init#4") == 0) + wait = "cttyid"; /* Ctty id port */ + else if (strcmp (wait, "init#5") == 0) + wait = "boot"; /* Bootstrap port */ + else + /* See if we can shorten the name to fit better. */ + { + char *abbrev = 0, *num = 0; + if (strncmp (wait, "fd#", 3) == 0) + abbrev = "fd", num = wait + 3; + else if (strncmp (wait, "bgfd#", 5) == 0) + abbrev = "bg", num = wait + 5; + else if (strncmp (wait, "port#", 5) == 0) + abbrev = "", num = wait + 5; + if (abbrev) + { + snprintf (port_name_buf, sizeof port_name_buf, + "%s%s", abbrev, num); + wait = port_name_buf; + } + } + + if (name) + snprintf (buf, sizeof buf, "%s:%s", wait, name); + else + snprintf (buf, sizeof buf, "%s:%d", wait, rpc); + + return ps_stream_write_field (stream, buf, field->width); + } + else + return ps_stream_write_field (stream, wait, field->width); +} +/* ---------------------------------------------------------------- */ +/* comparison functions */ + +/* Evaluates CALL if both s1 & s2 are non-NULL, and otherwise returns -1, 0, + or 1 ala strcmp, considering NULL to be less than non-NULL. */ +#define GUARDED_CMP(s1, s2, call) \ + ((s1) == NULL ? (((s2) == NULL) ? 0 : -1) : ((s2) == NULL ? 1 : (call))) + +int +ps_cmp_ints (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + int (*gf)() = G (getter, int); + int v1 = gf(ps1), v2 = gf (ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_floats (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + float (*gf)() = G (getter, float); + float v1 = gf(ps1), v2 = gf (ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_uids (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + struct ps_user *(*gf)() = G (getter, struct ps_user *); + struct ps_user *u1 = gf (ps1), *u2 = gf (ps2); + return (u1 ? ps_user_uid (u1) : -1) - (u2 ? ps_user_uid (u2) : -1); +} + +int +ps_cmp_unames (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + struct ps_user *(*gf)() = G (getter, struct ps_user *); + struct ps_user *u1 = gf (ps1), *u2 = gf (ps2); + struct passwd *pw1 = u1 ? ps_user_passwd (u1) : 0; + struct passwd *pw2 = u2 ? ps_user_passwd (u2) : 0; + return GUARDED_CMP (pw1, pw2, strcmp (pw1->pw_name, pw2->pw_name)); +} + +int +ps_cmp_strings (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + void (*gf)() = G (getter, void); + char *s1, *s2; + int s1len, s2len; + + /* Get both strings */ + gf (ps1, &s1, &s1len); + gf (ps2, &s2, &s2len); + + return GUARDED_CMP(s1, s2, strncmp(s1, s2, MIN (s1len, s2len))); +} + +int +ps_cmp_times (struct proc_stat *ps1, struct proc_stat *ps2, + const struct ps_getter *getter) +{ + void (*g)() = G (getter, void); + struct timeval tv1, tv2; + + g (ps1, &tv1); + g (ps2, &tv2); + + return + tv1.tv_sec > tv2.tv_sec ? 1 + : tv1.tv_sec < tv2.tv_sec ? -1 + : tv1.tv_usec > tv2.tv_usec ? 1 + : tv2.tv_usec < tv2.tv_usec ? -1 + : 0; +} + +/* ---------------------------------------------------------------- */ +/* `Nominal' functions -- return true for `unexciting' values. */ + +/* For many things, zero is not so interesting. */ +int +ps_nominal_zint (struct proc_stat *ps, const struct ps_getter *getter) +{ + return G (getter, int)(ps) == 0; +} + +/* Neither is an empty string. */ +int +ps_nominal_string (struct proc_stat *ps, const struct ps_getter *getter) +{ + char *str; + size_t len; + G (getter, char *)(ps, &str, &len); + return !str || len == 0 || (len == 1 && *str == '-'); +} + +/* Priorities are similar, but have to be converted to the unix nice scale + first. */ +int +ps_nominal_pri (struct proc_stat *ps, const struct ps_getter *getter) +{ + return MACH_PRIORITY_TO_NICE(G (getter, int)(ps)) == 0; +} + +/* Hurd processes usually have 2 threads; XXX is there someplace we get get + this number from? */ +int +ps_nominal_nth (struct proc_stat *ps, const struct ps_getter *getter) +{ + return G (getter, int)(ps) == 2; +} + +static int own_uid = -2; /* -1 means no uid at all. */ + +/* A user is nominal if it's the current user. */ +int +ps_nominal_user (struct proc_stat *ps, const struct ps_getter *getter) +{ + struct ps_user *u = G (getter, struct ps_user *)(ps); + if (own_uid == -2) + own_uid = getuid (); + return own_uid >= 0 && u && u->uid == own_uid; +} + +/* A uid is nominal if it's that of the current user. */ +int +ps_nominal_uid (struct proc_stat *ps, const struct ps_getter *getter) +{ + uid_t uid = G (getter, uid_t)(ps); + if (own_uid == -2) + own_uid = getuid (); + return own_uid >= 0 && uid == own_uid; +} + +/* ---------------------------------------------------------------- */ + +/* Returns the first entry called NAME in the vector of fmt_specs SPECS. If + the result is in fact an alias entry, returns in ALIASED_TO the name of + the desired source. */ +static const struct ps_fmt_spec * +specv_find (const struct ps_fmt_spec *specs, const char *name, + char **aliased_to) +{ + while (! ps_fmt_spec_is_end (specs)) + { + char *alias = index (specs->name, '='); + if (alias) + { + unsigned name_len = strlen (name); + + if (name_len == alias - specs->name + && strncasecmp (name, specs->name, name_len) == 0) + /* SPECS is an alias, lookup what it refs to. */ + { + *aliased_to = alias + 1; + return specs; + } + } + else + if (strcasecmp (specs->name, name) == 0) + return specs; + specs++; + } + + return 0; +} + +/* Number of specs allocated in each block of expansions. */ +#define EXP_BLOCK_SIZE 20 + +/* A node in a linked list of spec vectors. */ +struct ps_fmt_spec_block +{ + struct ps_fmt_spec_block *next; + struct ps_fmt_spec specs[EXP_BLOCK_SIZE]; +}; + +/* Adds a new alias expansion, using fields from ALIAS, where non-zero, + otherwise SRC, to SPECS. */ +struct ps_fmt_spec * +specs_add_alias (struct ps_fmt_specs *specs, + const struct ps_fmt_spec *alias, + const struct ps_fmt_spec *src) +{ + struct ps_fmt_spec *exp; + struct ps_fmt_spec_block *block; + char *name_end = index (alias->name, '='); + size_t name_len = name_end ? name_end - alias->name : strlen (alias->name); + + for (block = specs->expansions; block; block = block->next) + { + exp = block->specs; + while (! ps_fmt_spec_is_end (exp)) + exp++; + if (exp + 1 < block->specs + EXP_BLOCK_SIZE) + /* Found some empty space at EXP. */ + break; + } + + if (! block) + /* Ran out of blocks, we gotta make a new one. */ + { + block = malloc (sizeof (struct ps_fmt_spec_block)); + if (! block) + return 0; + block->next = specs->expansions; + specs->expansions = block; + exp = block->specs; + } + + /* EXP gets its name from ALIAS, but only the bit before the alias marker. */ + exp->name = malloc (name_len + 1); + if (! exp->name) + return 0; + bcopy ((char *)alias->name, (char *)exp->name, name_len); + ((char *)exp->name)[name_len] = '\0'; + + /* Copy the rest of the fields from ALIAS, but defaulting to SRC. */ + exp->title = alias->title ?: src->title; + exp->width = alias->width ?: src->width; + exp->precision = alias->precision >= 0 ? alias->precision : src->precision; + exp->flags = src->flags ^ alias->flags; + exp->getter = alias->getter ?: src->getter; + exp->output_fn = alias->output_fn ?: src->output_fn; + exp->cmp_fn = alias->cmp_fn ?: src->cmp_fn; + exp->nominal_fn = alias->nominal_fn ?: src->nominal_fn; + + /* Now add the list-end marker. */ + bzero (exp + 1, sizeof (*exp)); + + return exp; +} + +const struct ps_fmt_spec * +ps_fmt_specs_find (struct ps_fmt_specs *specs, const char *name) +{ + if (specs) /* Allow NULL to make recursion more handy. */ + { + struct ps_fmt_spec_block *block; + char *aliased_to = 0; + const struct ps_fmt_spec *s = 0; + + /* If SPECS contains any alias expansions, look there first. */ + for (block = specs->expansions; block && !s; block = block->next) + s = specv_find (block->specs, name, &aliased_to); + + if (! s) + /* Look in the local list of specs. */ + s = specv_find (specs->specs, name, &aliased_to); + + if (s) + { + if (aliased_to) + { + const struct ps_fmt_spec *src; /* What S is an alias to. */ + + if (strcasecmp (name, aliased_to) == 0) + /* An alias to the same name (useful to just change some + property) -- start looking up in the parent. */ + src = ps_fmt_specs_find (specs->parent, aliased_to); + else + src = ps_fmt_specs_find (specs, aliased_to); + + if (! src) + return 0; + + s = specs_add_alias (specs, s, src); + } + } + else + /* Try again with our parent. */ + s = ps_fmt_specs_find (specs->parent, name); + + return s; + } + else + return 0; +} + +/* ---------------------------------------------------------------- */ + +static const struct ps_fmt_spec specs[] = +{ + {"PID", 0, -5, -1, 0, + &ps_pid_getter, ps_emit_int, ps_cmp_ints, 0}, + {"TH", "TH#", -2, -1, 0, + &ps_thread_index_getter,ps_emit_int, ps_cmp_ints, 0}, + {"PPID", 0, -5, -1, 0, + &ps_ppid_getter, ps_emit_int, ps_cmp_ints, 0}, + {"UID", 0, -4, -1, PS_FMT_FIELD_KEEP, + &ps_owner_uid_getter, ps_emit_uid, ps_cmp_ints, ps_nominal_uid}, + {"User", 0, 8, -1, PS_FMT_FIELD_KEEP, + &ps_owner_getter, ps_emit_uname, ps_cmp_unames, ps_nominal_user}, + {"NTh", 0, -2, -1, 0, + &ps_num_threads_getter, ps_emit_int, ps_cmp_ints, ps_nominal_nth}, + {"PGrp", 0, -5, -1, 0, + &ps_pgrp_getter, ps_emit_int, ps_cmp_ints, 0}, + {"Sess", 0, -5, -1, 0, + &ps_session_getter, ps_emit_int, ps_cmp_ints, 0}, + {"LColl", 0, -5, -1, 0, + &ps_login_col_getter, ps_emit_int, ps_cmp_ints, 0}, + {"Args", 0, 0, -1, 0, + &ps_args_getter, ps_emit_args, ps_cmp_strings,ps_nominal_string}, + {"Arg0", 0, 0, -1, 0, + &ps_args_getter, ps_emit_string, ps_cmp_strings,ps_nominal_string}, + {"Env", 0, 0, -1, 0, + &ps_env_getter, ps_emit_args, ps_cmp_strings,ps_nominal_string}, + {"Start", 0, -7, 1, 0, + &ps_start_time_getter, ps_emit_past_time, ps_cmp_times,0}, + {"Time", 0, -8, 2, 0, + &ps_tot_time_getter, ps_emit_seconds, ps_cmp_times, 0}, + {"UTime", 0, -8, 2, 0, + &ps_usr_time_getter, ps_emit_seconds, ps_cmp_times, 0}, + {"STime", 0, -8, 2, 0, + &ps_sys_time_getter, ps_emit_seconds, ps_cmp_times, 0}, + {"VSize", 0, -5, -1, 0, + &ps_vsize_getter, ps_emit_nice_int,ps_cmp_ints, 0}, + {"RSize", 0, -5, -1, 0, + &ps_rsize_getter, ps_emit_nice_int,ps_cmp_ints, 0}, + {"Pri", 0, -3, -1, 0, + &ps_cur_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri}, + {"BPri", 0, -3, -1, 0, + &ps_base_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri}, + {"MPri", 0, -3, -1, 0, + &ps_max_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri}, + {"Mem", "%Mem", -4, -1, 0, + &ps_rmem_frac_getter, ps_emit_percent, ps_cmp_floats, 0}, + {"CPU", "%CPU", -4, -1, 0, + &ps_cpu_frac_getter, ps_emit_percent, ps_cmp_floats, 0}, + {"State", 0, 4, -1, 0, + &ps_state_getter, ps_emit_state, 0, 0}, + {"Wait", 0, 10, -1, 0, + &ps_wait_getter, ps_emit_wait, 0, 0}, + {"Sleep", 0, -2, -1, 0, + &ps_sleep_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"Susp", 0, -2, -1, 0, + &ps_susp_count_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"PSusp", 0, -2, -1, 0, + &ps_proc_susp_count_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"TSusp", 0, -2, -1, 0, + &ps_thread_susp_count_getter, ps_emit_int,ps_cmp_ints, ps_nominal_zint}, + {"TTY", 0, -2, -1, 0, + &ps_tty_getter, ps_emit_tty_name,ps_cmp_strings,0}, + {"PgFlts", 0, -5, -1, 0, + &ps_page_faults_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"COWFlts", 0, -5, -1, 0, + &ps_cow_faults_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"PgIns", 0, -5, -1, 0, + &ps_pageins_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"MsgIn", 0, -5, -1, 0, + &ps_msgs_rcvd_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"MsgOut", 0, -5, -1, 0, + &ps_msgs_sent_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {"ZFills", 0, -5, -1, 0, + &ps_zero_fills_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint}, + {0} +}; + +struct ps_fmt_specs ps_std_fmt_specs = { specs, 0 }; diff --git a/libps/tty.c b/libps/tty.c new file mode 100644 index 00000000..3ab72ee8 --- /dev/null +++ b/libps/tty.c @@ -0,0 +1,155 @@ +/* The ps_tty type, for per-tty info. + + Copyright (C) 1995,1996,2000 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <hurd/term.h> + +#include "ps.h" +#include "common.h" + +#include "ps_term.h" + +/* ---------------------------------------------------------------- */ + +/* Create a ps_tty for the tty referred to by PORT, returning it in TTY. + If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t +ps_tty_create (file_t port, struct ps_tty **tty) +{ + *tty = NEW (struct ps_tty); + if (*tty == NULL) + return ENOMEM; + + (*tty)->port = port; + (*tty)->name_state = PS_TTY_NAME_PENDING; + (*tty)->short_name = NULL; + (*tty)->short_name_alloced = FALSE; + + return 0; +} + +/* Frees TTY and any resources it consumes. */ +void +ps_tty_free (struct ps_tty *tty) +{ + mach_port_deallocate(mach_task_self (), tty->port); + if (tty->name_state == PS_TTY_NAME_OK && tty->name != NULL) + free ((char *)tty->name); + if (tty->short_name_alloced) + free ((char *)tty->short_name); + free (tty); +} + +/* ---------------------------------------------------------------- */ + +/* Returns the name of the tty, or NULL if it can't be figured out. */ +const char * +ps_tty_name (struct ps_tty *tty) +{ + if (tty->name_state == PS_TTY_NAME_PENDING) + { + string_t buf; + + if (ps_term_get_nodename (tty->port, buf) != 0) + /* There is a terminal there, but we can't figure out its name. */ + tty->name_state = PS_TTY_NAME_ERROR; + else + { + tty->name = strdup (buf); + tty->name_state = (tty->name ? PS_TTY_NAME_OK : PS_TTY_NAME_ERROR); + } + } + + if (tty->name_state == PS_TTY_NAME_OK) + return tty->name; + else + return NULL; +} + +/* ---------------------------------------------------------------- */ + +struct ps_tty_abbrev +{ + const char *pfx; + const char *subst; +}; + +const struct ps_tty_abbrev ps_tty_abbrevs[] = +{ + { "/tmp/console", "oc" }, /* temp hack */ + { "/dev/console", "co" }, + { "/dev/tty", "" }, + { "/dev/pty", "" }, + { "/dev/com", "c" }, + { "/dev/", "" }, + { 0 } +}; + +/* Returns the standard abbreviated name of the tty, the whole name if there + is no standard abbreviation, or NULL if it can't be figured out. */ +const char * +ps_tty_short_name (struct ps_tty *tty) +{ + if (tty->short_name != NULL) + return tty->short_name; + else + { + const struct ps_tty_abbrev *abbrev; + const char *name = ps_tty_name (tty); + + if (name) + for (abbrev = ps_tty_abbrevs; abbrev->pfx != NULL; abbrev++) + { + const char *subst = abbrev->subst; + size_t pfx_len = strlen (abbrev->pfx); + + if (strncmp (name, abbrev->pfx, pfx_len) == 0) + { + if (name[pfx_len] == '\0') + tty->short_name = abbrev->subst; + else if (!subst || subst[0] == '\0') + tty->short_name = name + pfx_len; + else + { + size_t slen = strlen (subst); + size_t nlen = strlen (name + pfx_len) + 1; + char *n = malloc (slen + nlen); + if (n) + { + memcpy (n, subst, slen); + memcpy (&n[slen], &name[pfx_len], nlen); + tty->short_name = n; + tty->short_name_alloced = TRUE; + } + } + break; + } + } + + if (tty->short_name == NULL) + tty->short_name = name; + + return tty->short_name; + } +} diff --git a/libps/user.c b/libps/user.c new file mode 100644 index 00000000..4af5014f --- /dev/null +++ b/libps/user.c @@ -0,0 +1,162 @@ +/* The ps_user type, for per-user info. + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "ps.h" +#include "common.h" + +static error_t +install_passwd (struct ps_user *u, struct passwd *pw) +{ + int needed = 0; + +#define COUNT(field) if (pw->field != NULL) (needed += strlen(pw->field) + 1) + COUNT (pw_name); + COUNT (pw_passwd); + COUNT (pw_gecos); + COUNT (pw_dir); + COUNT (pw_shell); + + u->storage = malloc (needed); + if (u->storage != NULL) + { + char *p = u->storage; + + /* Copy each string field into storage allocated in the u + structure and point the fields at that instead of the static + storage that pw currently points to. */ +#define COPY(field) \ +if (pw->field != NULL) \ +strcpy(p, pw->field), (pw->field = p), (p += strlen (p) + 1) + COPY (pw_name); + COPY (pw_passwd); + COPY (pw_gecos); + COPY (pw_dir); + COPY (pw_shell); + + u->passwd = *pw; + + return 0; + } + else + return ENOMEM; +} + +/* Create a ps_user for the user referred to by UID, returning it in U. + If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */ +error_t +ps_user_create (uid_t uid, struct ps_user **u) +{ + *u = NEW (struct ps_user); + if (*u == NULL) + return ENOMEM; + + (*u)->uid = uid; + (*u)->passwd_state = PS_USER_PASSWD_PENDING; + + return 0; +} + +/* Create a ps_user for the user referred to by UNAME, returning it in U. + If a memory allocation error occurs, ENOMEM is returned. If no such user + is known, EINVAL is returned. */ +error_t +ps_user_uname_create (char *uname, struct ps_user **u) +{ + struct passwd *pw = getpwnam (uname); + if (pw) + return ps_user_passwd_create (pw, u); + else + return EINVAL; +} + +/* Makes makes a ps_user containing PW (which is copied). */ +error_t +ps_user_passwd_create (struct passwd *pw, struct ps_user **u) +{ + error_t err = 0; + + *u = NEW (struct ps_user); + if (*u == NULL) + err = ENOMEM; + else + { + err = install_passwd (*u, pw); + if (err) + FREE (*u); + else + { + (*u)->passwd_state = PS_USER_PASSWD_OK; + (*u)->uid = pw->pw_uid; + } + } + + return err; +} + +/* Free U and any resources it consumes. */ +void +ps_user_free (struct ps_user *u) +{ + if (u->passwd_state == PS_USER_PASSWD_OK) + free (u->storage); + free (u); +} + +/* ---------------------------------------------------------------- */ + +/* Returns the password file entry (struct passwd, from <pwd.h>) for the user + referred to by U, or NULL if it can't be gotten. */ +struct passwd *ps_user_passwd (struct ps_user *u) +{ + if (u->passwd_state == PS_USER_PASSWD_OK) + return &u->passwd; + else if (u->passwd_state == PS_USER_PASSWD_ERROR) + return NULL; + else + { + struct passwd *pw = getpwuid (u->uid); + if (pw != NULL && install_passwd (u, pw) == 0) + { + u->passwd_state = PS_USER_PASSWD_OK; + return &u->passwd; + } + else + { + u->passwd_state = PS_USER_PASSWD_ERROR; + return NULL; + } + } +} + +/* Returns the user name for the user referred to by U, or NULL if it can't + be gotten. */ +char *ps_user_name (struct ps_user *u) +{ + struct passwd *pw = ps_user_passwd (u); + if (pw) + return pw->pw_name; + else + return NULL; +} diff --git a/libps/write.c b/libps/write.c new file mode 100644 index 00000000..83be42ab --- /dev/null +++ b/libps/write.c @@ -0,0 +1,264 @@ +/* Ps stream output + + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "ps.h" +#include "common.h" + +/* True if CH is a `control character'. */ +#define iscntl(ch) ((unsigned)(ch) < 32) + +/* *BEG and NEW - 1 are the bounds of a buffer, which write to S (the last + character just before NEW isn't included, because something different + about it is what caused the flush), and update *BEG to be NEW. True is + returned if a write error occurs. */ +static int +flush (const char **beg, const char *new, FILE *s) +{ + const char *b = *beg; + if (new > b) + *beg = new; + if (new - 1 > b) + { + size_t len = new - 1 - b; + int ret = fwrite (b, 1, len, s); + if (ret < len) + return 1; + } + return 0; +} + +/* Write T to S, up to MAX characters (unless MAX == 0), making sure not to + write any unprintable characters. */ +error_t +noise_write (const unsigned char *t, ssize_t max, FILE *s) +{ + int ch; + const char *ok = t; + size_t len = 0; + + while ((ch = *t++) && (max < 0 || len < max)) + if (isgraph (ch) || ch == ' ') + len++; + else + { + int is_cntl = iscntl (ch); + + if (flush (&ok, t, s)) + return errno; + + len += (is_cntl ? 2 : 4); + if (max >= 0 && len > max) + break; + + if (is_cntl) + fprintf (s, "^%c", ch + 'A'); + else + fprintf (s, "\\%03o", ch); + } + + if (flush (&ok, t, s)) + return errno; + + return 0; +} + +/* Return what noise_write would write with arguments of T and MAX. */ +size_t +noise_len (const char *t, ssize_t max) +{ + int ch; + size_t len = 0; + + while ((ch = *t++) && (max == 0 || len < max)) + if (isgraph (ch) || ch == ' ') + len++; + else + { + size_t rep_len = iscntl (ch) ? 2 : 4; + if (max >= 0 && rep_len + len > max) + break; + len += rep_len; + } + + return len; +} + +/* ---------------------------------------------------------------- */ + +/* Write at most MAX_LEN characters of STRING to STREAM (if MAX_LEN > the + length of STRING, then write all of it; if MAX_LEN == -1, then write all + of STRING regardless). */ +error_t +ps_stream_write (struct ps_stream *stream, const char *string, ssize_t max_len) +{ + size_t len = noise_len (string, max_len); + + if (len > 0) + { + error_t err; + ssize_t spaces_needed = stream->spaces; + + stream->spaces = 0; + while (spaces_needed > 0) + { + static char spaces[] = " "; +#define spaces_len (sizeof(spaces) - 1) + size_t chunk = spaces_needed > spaces_len ? spaces_len : spaces_needed; + error_t err = + ps_stream_write (stream, spaces + spaces_len - chunk, chunk); + if (err) + return err; + spaces_needed -= chunk; + } + stream->spaces = spaces_needed; + + err = noise_write (string, len, stream->stream); + if (err) + return err; + + stream->pos += len; + } + + return 0; +} + +/* Write NUM spaces to STREAM. NUM may be negative, in which case the same + number of adjacent spaces (written by other calls to ps_stream_space) are + consumed if possible. If an error occurs, the error code is returned, + otherwise 0. */ +error_t +ps_stream_space (struct ps_stream *stream, ssize_t num) +{ + stream->spaces += num; + return 0; +} + +/* Write as many spaces to STREAM as required to make a field of width SOFAR + be at least WIDTH characters wide (the absolute value of WIDTH is used). + If an error occurs, the error code is returned, otherwise 0. */ +error_t +ps_stream_pad (struct ps_stream *stream, ssize_t sofar, ssize_t width) +{ + return ps_stream_space (stream, ABS (width) - sofar); +} + +/* Write a newline to STREAM, resetting its position to zero. */ +error_t +ps_stream_newline (struct ps_stream *stream) +{ + putc ('\n', stream->stream); + stream->spaces = 0; + stream->pos = 0; + return 0; +} + +/* Write the string BUF to STREAM, padded on one side with spaces to be at + least the absolute value of WIDTH long: if WIDTH >= 0, then on the left + side, otherwise on the right side. If an error occurs, the error code is + returned, otherwise 0. */ +error_t +_ps_stream_write_field (struct ps_stream *stream, + const char *buf, size_t max_width, + int width) +{ + error_t err; + size_t len; + + while (isspace (*buf)) + buf++; + + if (stream->spaces < 0 && max_width >= 0) + /* Take some of our spacing deficit out of a truncatable field. */ + max_width += stream->spaces; + + len = noise_len (buf, max_width); + + if (width > 0) + { + err = ps_stream_write (stream, buf, len); + if (!err) + err = ps_stream_space (stream, width - len); + } + else if (width < 0) + { + err = ps_stream_space (stream, -width - len); + if (!err) + err = ps_stream_write (stream, buf, len); + } + else + err = ps_stream_write (stream, buf, len); + + return err; +} + +/* Write the string BUF to STREAM, padded on one side with spaces to be at + least the absolute value of WIDTH long: if WIDTH >= 0, then on the left + side, otherwise on the right side. If an error occurs, the error code is + returned, otherwise 0. */ +error_t +ps_stream_write_field (struct ps_stream *stream, const char *buf, int width) +{ + return _ps_stream_write_field (stream, buf, -1, width); +} + +/* Like ps_stream_write_field, but truncates BUF to make it fit into WIDTH. */ +error_t +ps_stream_write_trunc_field (struct ps_stream *stream, + const char *buf, int width) +{ + return _ps_stream_write_field (stream, buf, width ? ABS (width) : -1, width); +} + +/* Write the decimal representation of VALUE to STREAM, padded on one side + with spaces to be at least the absolute value of WIDTH long: if WIDTH >= + 0, then on the left side, otherwise on the right side. If an error + occurs, the error code is returned, otherwise 0. */ +error_t +ps_stream_write_int_field (struct ps_stream *stream, int value, int width) +{ + char buf[20]; + sprintf (buf, "%d", value); + return ps_stream_write_field (stream, buf, width); +} + +/* Create a stream outputing to DEST, and return it in STREAM, or an error. */ +error_t +ps_stream_create (FILE *dest, struct ps_stream **stream) +{ + *stream = malloc (sizeof (struct ps_stream)); + if (! *stream) + return ENOMEM; + (*stream)->stream = dest; + (*stream)->spaces = 0; + (*stream)->pos = 0; + return 0; +} + +/* Frees STREAM. The destination file is *not* closed. */ +void +ps_stream_free (struct ps_stream *stream) +{ + free (stream); +} |