aboutsummaryrefslogtreecommitdiff
path: root/libps
diff options
context:
space:
mode:
Diffstat (limited to 'libps')
-rw-r--r--libps/ChangeLog885
-rw-r--r--libps/Makefile41
-rw-r--r--libps/common.h44
-rw-r--r--libps/context.c165
-rw-r--r--libps/filters.c91
-rw-r--r--libps/fmt.c573
-rw-r--r--libps/host.c112
-rw-r--r--libps/proclist.c692
-rw-r--r--libps/procstat.c1137
-rw-r--r--libps/ps.h1047
-rw-r--r--libps/pshost.h53
-rw-r--r--libps/spec.c1152
-rw-r--r--libps/tty.c155
-rw-r--r--libps/user.c162
-rw-r--r--libps/write.c264
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);
+}