summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/ChangeLog1765
-rw-r--r--utils/Makefile80
-rw-r--r--utils/addauth.c100
-rw-r--r--utils/devprobe.c107
-rw-r--r--utils/frobauth-mod.c160
-rw-r--r--utils/frobauth.c282
-rw-r--r--utils/frobauth.doc83
-rw-r--r--utils/frobauth.h71
-rw-r--r--utils/fsysopts.c128
-rw-r--r--utils/ftpcp.c397
-rw-r--r--utils/ftpdir.c330
-rw-r--r--utils/ids.c196
-rw-r--r--utils/login.c898
-rw-r--r--utils/loginpr.sh20
-rw-r--r--utils/mount.c575
-rw-r--r--utils/msgport.c678
-rw-r--r--utils/nonsugid.c63
-rw-r--r--utils/parse.c185
-rw-r--r--utils/parse.h63
-rw-r--r--utils/pids.c232
-rw-r--r--utils/pids.h47
-rw-r--r--utils/ping.c1156
-rw-r--r--utils/portinfo.c226
-rw-r--r--utils/ps.c442
-rw-r--r--utils/psout.c144
-rw-r--r--utils/psout.h34
-rw-r--r--utils/rmauth.c121
-rw-r--r--utils/rpctrace.c713
-rw-r--r--utils/setauth.c134
-rw-r--r--utils/settrans.c216
-rw-r--r--utils/shd.c379
-rw-r--r--utils/showtrans.c145
-rw-r--r--utils/storecat.c69
-rw-r--r--utils/storeinfo.c255
-rw-r--r--utils/storeread.c120
-rw-r--r--utils/su.c2
-rw-r--r--utils/sush.sh90
-rw-r--r--utils/syncfs.c80
-rw-r--r--utils/unsu.c90
-rw-r--r--utils/uptime.sh62
-rw-r--r--utils/vminfo.c242
-rw-r--r--utils/vmstat.c632
-rw-r--r--utils/w.c519
-rw-r--r--utils/x.c248
44 files changed, 12579 insertions, 0 deletions
diff --git a/utils/ChangeLog b/utils/ChangeLog
new file mode 100644
index 00000000..a9049a23
--- /dev/null
+++ b/utils/ChangeLog
@@ -0,0 +1,1765 @@
+2000-01-22 Roland McGrath <roland@baalperazim.frob.com>
+
+ * nonsugid.c (get_nonsugid_ids): Remove saved set IDs from available
+ sets before merging effective sets.
+
+1999-11-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * storeinfo.c (print_store: pstr): Add const to parameter type.
+
+1999-11-19 Roland McGrath <roland@baalperazim.frob.com>
+
+ * mount.c: New file.
+ * mount.sh: File removed.
+ * Makefile (special-targets): Remove mount.
+ (SRCS): mount.sh -> mount.c
+ (mount): Add deps on ../sutils/fstab.o, ../sutils/clookup.o,
+ libfshelp and libports.
+ (../sutils/fstab.o ../sutils/clookup.o): Rule to build in ../sutils.
+
+1999-11-18 Roland McGrath <roland@baalperazim.frob.com>
+
+ * vmstat.c (BADVAL): New macro.
+ (SWAP_FIELD): New macro. This properly takes care of exciting C
+ type promotion rules to properly indicate errors.
+ (get_swap_size, get_swap_free, get_swap_active): Replace definitions
+ with ones using SWAP_FIELD macro.
+ (ensure_def_pager_info): Give an error message for lack of a default
+ pager; check only once.
+
+1999-09-23 Mark Kettenis <kettenis@gnu.org>
+
+ * login.c (main): Pass controlling terminal ID port to the child.
+
+1999-09-19 Roland McGrath <roland@baalperazim.frob.com>
+
+ * w.c (add_utmp_procs): Ignore entries whose ut_type is not
+ LOGIN_PROCESS or USER_PROCESS, or whose ut_line is empty.
+
+1999-08-10 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * portinfo.c: Correct documentation for (currently implemented)
+ --target-send and --target-send-once options. Reported by Marcus
+ Brinkmann (Marcus.Brinkmann@ruhr-uni-bochum.de).
+
+1999-07-23 Roland McGrath <roland@baalperazim.frob.com>
+
+ * Makefile (targets): Remove freeauth, added accidentally.
+
+1999-07-17 Roland McGrath <roland@baalperazim.frob.com>
+
+ * rpctrace.c (rewrite_right): If we see our own wrapper right,
+ unwrap and replace it with the original send right.
+ (trace_and_forward): Remove vm_map special case.
+
+ * rpctrace.c: Mostly rewritten. Now mostly working with skeletal
+ functionality.
+
+1999-07-15 Roland McGrath <roland@baalperazim.frob.com>
+
+ * rpctrace.c: New file, from josem and gord. Not yet functional.
+ * Makefile (rpctrace): New target.
+ (targets): Add rpctrace.
+ (SRCS): Add rpctrace.c.
+
+1999-07-10 Roland McGrath <roland@baalperazim.frob.com>
+
+ * pids.c: Add #include <sys/mman.h> for munmap decl.
+ * showtrans.c: Likewise.
+ * login.c: Likewise.
+ * storeread.c: Likewise.
+ * msgport.c: Likewise.
+
+1999-07-03 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * login.c (cat): Use munmap instead of vm_deallocate.
+ (check_owned): Likewise.
+ (kill_login): Likewise.
+ * storeread.c (main): Likewise.
+ * showtrans.c (main): Likewise.
+ * pids.c (add_fn_pids): Likewise.
+ * msgport.c (cmd_getenv): Likewise.
+
+1999-07-01 Mark Kettenis <kettenis@gnu.org>
+
+ * Makefile (INSTALL-addauth-ops, INSTALL-setauth-ops,
+ INSTALL-su-ops): Removed. These programs don't have to be
+ installed suid-root anymore.
+ * frobauth-mod.c (frobauth_modify): Do not try to set the owner of
+ the processes we're frobbing. The process set their owner
+ themselves when we pass them their new authentication.
+
+1999-06-21 Roland McGrath <roland@baalperazim.frob.com>
+
+ * login.c (main): For no-uids shell, default SH_ARG0 to informative
+ value. Use asprintf to construct it.
+
+1999-06-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * ps.c (options): Doc fix.
+
+1999-05-29 Roland McGrath <roland@baalperazim.frob.com>
+
+ * w.c (fetch_boot_time): New function. Use libps to get
+ task_basic_info for PID 1 (init); its creation_time is "boot time".
+ (uptime): Use it, instead of stat'ing /var/run/uptime.
+
+ * ps.c (output_fmts): Add %start and %time specs before %command
+ in "user" (ps -u) format.
+
+1999-05-16 Roland McGrath <roland@baalperazim.frob.com>
+
+ * portinfo.c (parse_task): Fail with appropriate error message if the
+ fetched task is MACH_PORT_NULL.
+
+1999-04-25 Roland McGrath <roland@baalperazim.frob.com>
+
+ * shd.c (run): Print child's PID when pausing before exec.
+
+Fri Mar 12 15:22:02 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * login.c (add_utmp_entry): Properly call setutent and endutent
+ around the getutline call.
+
+1999-03-11 Roland McGrath <roland@baalperazim.frob.com>
+
+ * portinfo.c (main): Grok -E and inhibit error msg for
+ print_xlated_port_info if set.
+ (options): #if 0 -q option until we implement it.
+
+1999-02-17 Roland McGrath <roland@baalperazim.frob.com>
+
+ * mount.sh: Use `awk' instead of `gawk'.
+
+Fri Feb 19 02:43:11 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * login.c (main): Correctly implement -f instead of always
+ acting as if it were set.
+
+ * login.c (main): When transitioning from no UID's to having UID's
+ create a new session like normal. Reported by Mark M. Kettenis
+ <kettenis@wins.uva.nl>.
+
+1999-02-06 Mark Kettenis <kettenis@gnu.org>
+
+ * login.c (main): If no effective uid, really use first auxiliary
+ uid to use as password entry to get parameters from.
+
+1999-01-03 Roland McGrath <roland@baalperazim.frob.com>
+
+ * syncfs.c: Replace -a/--asynchronous with -s/--synchronous.
+ Default is now asynchronous.
+
+1998-12-31 Roland McGrath <roland@baalperazim.frob.com>
+
+ * Makefile (SRCS, targets, targets rule): Rename sync to syncfs.
+ * syncfs.c: Renamed from sync.c.
+ Take flags -a/--asynchronous and -c/--no-children,
+ and optional file arguments. Diagnose errors.
+
+1998-10-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * Makefile (OBJS): Use % pattern instead of explicit list.
+
+ * parse.c (parse_enum): Add braces to silence gcc warning.
+ * psout.c (psout): Likewise.
+ * settrans.c (main): Likewise.
+ * showtrans.c (main): Likewise.
+ * w.c (w_fetch): Likewise.
+ * vminfo.c (main): Likewise.
+
+1998-09-05 Roland McGrath <roland@baalperazim.frob.com>
+
+ * ping.c: <linux/icmp.h> -> <netinet/ip_icmp.h>;
+ struct icmphdr -> struct icmp;
+ Remove macro hacks to cope with old headers.
+ (pinger_wrapper): Take and return void *, so we're a cthread_fn_t.
+
+1998-09-04 Roland McGrath <roland@baalperazim.frob.com>
+
+ * portinfo.c (hold): Declare explicit `int' to silence warning.
+
+ * Makefile (OBJS): Use pattern %.sh instead of explicit list.
+
+Fri Aug 21 19:25:12 1998 Jose M. Moya <josem@gnu.org>
+
+ * msgport.c: Comments added.
+ (add_cmd): cmd_add renamed to add_cmd.
+ (parse_cmd_opt): cmd_parse_opt renamed to parse_cmd_opt.
+
+Mon Aug 3 17:10:05 1998 Joel N. Weber II <devnull@theobromine.ai.mit.edu>
+
+ * Makefile (msgport): Add dependency on libshouldbeinlibc.
+
+1998-07-25 Jose M. Moya <josem@gnu.org>
+
+ * msgport.c: Fixed to deallocate ports.
+
+1998-07-24 Jose M. Moya <josem@gnu.org>
+
+ * msgport.c: New file.
+ * Makefile (targets): Add `msgport'.
+ (SRCS): Add `msgport.c'.
+ (msgport): Add dependencies on parse.o and pids.o.
+
+1998-07-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * ps.c (main): Fix return type to int, and use return.
+ * ids.c (main): Likewise.
+ * w.c (main): Likewise.
+ * login.c (main): Likewise.
+ * settrans.c (main): Likewise.
+ * showtrans.c (main): Likewise.
+ * fsysopts.c (main): Likewise.
+ * storeinfo.c (main): Likewise.
+ * unsu.c (main): Likewise.
+ * setauth.c (main): Likewise.
+ * rmauth.c (main): Likewise.
+ * addauth.c (main): Likewise.
+
+ * w.c (main): Avoid warning from `while (a = b)'.
+
+Wed Apr 22 16:38:59 1998 Thomas Bushnell, n/BSG <tb@mit.edu>
+
+ * ping.c (MAXHOSTNAMELEN): Don't define.
+ (main): Delete variable `hnamebuf'. Store directly into properly
+ malloced `hostname' variable.
+
+Wed Apr 22 16:29:18 1998 Thomas Bushnell, n/BSG <tb@mit.edu>
+
+ * ping.c: New file. Originally P.D. from US Army; then modified
+ by BSD, then added to Linux. Taken from Linux ping and adapted
+ for Hurd and submitted for inclusion by Kunihiro Ishiguro
+ (kunihiro@zebra.org).
+ * Makefile (targets): Add `ping'.
+ (SRCS): Add `ping.c'.
+ (ping-CPPFLAGS): New variable, to find <linux/icmp.h> in the
+ pfinet sources.
+ (INSTALL-ping-ops): Install ping suid root.
+ (ping): Depend on `../libthreads/libthreads.a'.
+
+1997-09-29 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * unsu.c (argp_program_version): Use correct program name.
+
+1997-09-26 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * frobauth-mod.c (frobauth_modify): Add AUTHS & NUM_AUTHS parameters.
+ * frobauth.h (frobauth_modify): Add AUTHS & NUM_AUTHS parameters.
+ * rmauth.c (main): Supply new args to frobauth_modify.
+ * unsu.c (main): Likewise.
+ * setauth.c (main): Likewise.
+ * login.c (main): Use ugids_verify_make_auth instead of
+ ugids_verify and ugids_make_auth.
+ * addauth.c (main): Likewise.
+ * setauth.c (main): Likewise.
+
+ * w.c (main): Remove DEFAULT_OUTPUT variable.
+
+1997-08-08 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ftpcp.c (eappend, ebasename): New functions.
+ (append_basename): New function.
+ (main): Move target directory handling to append_basename.
+ Use it also with remote transfers.
+
+ * ftpcp.c (cntl_debug): Print CONN->hook as a string before any output.
+ (main): Set the connection hooks to appropriate strings.
+ (econnect): Add NAME parameter, & set connection hook from it.
+
+1997-07-29 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ftpcp.c (main): When mungeing the destination to copy into a
+ directory, do the name used to print error messages too.
+
+1997-07-15 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (ensure_def_pager_info): Don't report any errors if
+ there's no default pager.
+
+1997-07-10 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (main): Don't print fields we cannot get.
+
+1997-07-03 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ftpdir.c (main): Flush stdout after printing prefix/separator.
+
+1997-06-19 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storecat.c, storeread.c: Moved here from ../libstore.
+ * Makefile (SRCS): Add storecat.c & storeread.c.
+ (targets): Add storecat & storeread.
+ Add storecat & storeread to appropriate dependency rules.
+
+1997-06-13 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (main): Print the translated-node name instead of the
+ translator name if the translator startup error is due to opening it.
+
+1997-06-10 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ids.c (argp_program_version): Change name to `ids'.
+
+Mon Jun 9 12:45:24 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (SRCS): Add setauth.c, pids.c, unsu.c, and nonsugid.c.
+
+Wed Jun 4 14:26:53 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ftpcp.c, ftpdir.c: Files moved from ../libftpconn.
+ * Makefile (HURDLIBS): Add ftpconn.
+ (ftpcp ftpdir): Add dependency on ../liftpconn/libftpconn.a.
+ (SRCS): Add ftpcp.c & ftpdir.c.
+ (targets): Add ftpcp & ftpdir.
+
+Tue May 27 14:55:30 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (args_doc): Variable removed.
+ (main): Don't use ARGS_DOC.
+ Correct filtering of process-owners when processes are explicitly
+ specified.
+
+Mon May 26 02:21:19 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * su.c, addauth.c: Totally rewritten.
+ * rmauth.c, setauth.c, unsu.c, frobauth.h, frobauth.c,
+ frobauth-mod.c, nonsugid.c, pids.c, pids.h: New files.
+ * Makefile: Add appropriate dependencies on parse.o, pids.o,
+ frobauth.o, & frobauth-mod.o.
+ (addauth-LDLIBS, setauth-LDLIBS, su-LDLIBS): New variables.
+
+ * ids.c: Renamed from hurdids.c.
+ * Makefile (targets): hurdids renamed to ids.
+ (SRCS): hurdids.c renamed to ids.c.
+ (INSTALL-ids-ops): Renamed from INSTALL-hurdids-ops.
+
+ * login.c (child_argps): New variable.
+ (options): Remove uid/gid args.
+ (main): Use ugids operations instead of doing [ug]id frobbing
+ ourselves.
+ <ugids.h>: New include.
+
+ * ps.c (options): Rearranged to group similar options.
+ Process-selection options removed.
+ (current_lid, current_sid): Functions removed.
+ (lookup_user): Take new STATE argument.
+ (main): Use PIDS_ARGP to select processes.
+ Use new forms of parse_ functions.
+ "pids.h": New include.
+
+Tue May 20 14:38:22 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * parse.c (parse_enum, parse_numlist, parse_strlist, _parse_strlist):
+ Change void * HOOK parameters to struct argp_state *STATE.
+ * parse.h (parse_enum, parse_numlist, parse_strlist): Likewise.
+
+Mon May 19 23:04:54 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * parse.c (parse_enum, parse_numlist, parse_strlist, _parse_strlist):
+ Add HOOK argument, and pass it to callback functions.
+ * parse.h (parse_enum, parse_numlist, parse_strlist): Likewise.
+
+Thu May 15 14:04:29 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (procset_names, fmt_sortkeys, fmt_names, fmts): Variables
+ removed.
+ (enum procsets): Type removed.
+ (struct output_fmt): New type.
+ (output_fmt): New variable.
+ (parse_enum): Function removed.
+ (main): Use new version of parse_enum & output_fmts variable.
+
+ * login.c, addauth.c (main: verify_passwd): Make
+ user-in-group-0-can-use-his-own-password-to-su work.
+
+ * parse.c, parse.h: New files.
+ * addauth.c: Replace include of "psout.h" with one of "parse.h".
+ (_parse_strlist, parse_numlist): Functions removed.
+ * ps.c: Include "parse.h".
+ (_parse_strlist, parse_strlist, parse_numlist): Functions removed.
+ * Makefile (SRCS): Add parse.c.
+ (LCLHDRS): Add parse.h.
+ Add dependencies on parse.o.
+
+ * addauth.c: Renamed from addu.c.
+ * Makefile: Change references to addu into ones to addauth.
+
+Fri Apr 25 13:06:29 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * hurdids.c (main): Use the parent process's auth port instead of
+ the hurdids process's, because hurdids is usually installed setuid.
+
+ * login.c, addu.c (main: verify_passwd): Add
+ user-in-group-0-can-use-his-own-password-to-su hack.
+
+Wed Apr 16 15:59:41 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * login.c (add_utmp_entry): Revert change of December 10, 1996.
+
+Fri Mar 7 11:53:02 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * addu.c: New file.
+ * Makefile: Add addu to dependents of libshouldbeinlibc & libps.
+ (INSTALL-hurdids-ops, INSTALL-addu-ops): New variables.
+ (SRCS): Add addu.c.
+ (targets): Add addu.
+
+ * hurdids.c (args_doc): Initialize with "[PID]".
+ (main): Get the ids from the auth port instead of using libc grot.
+ Support getting ids from other processes.
+
+Sat Mar 1 21:27:03 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (add_utmp_procs): Ignore unused utmp entries.
+ (main): Support USER... args.
+
+Wed Feb 26 17:47:04 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * devprobe.c (main): Fetch DEVICE_MASTER *before* we use it.
+ Use argp_failure instead of error.
+ Open device read-only.
+
+Wed Feb 19 22:16:40 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * fsysopts.c, hurdids.c, login.c, portinfo.c, ps.c, settrans.c,
+ showtrans.c, storeinfo.c, sync.c, vminfo.c, vmstat.c, w.c
+ (argp_program_version): Make const.
+
+ * devprobe.c (main): Parse arguments before getting device master port.
+
+Tue Feb 11 20:51:51 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (options): Update to reflect some minor changes in
+ the way argp works.
+
+Thu Feb 6 01:40:19 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Look for NOBODY in ARGS_DEFS too, and use a last
+ ditch default if we can't find it anywhere.
+ Deal with crypt failing.
+
+ * w.c (w_get_host): Don't use strlen on it if *HOST is 0.
+
+Mon Dec 16 20:52:19 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * psout.c (psout): Use last N processes if TOP is negative.
+ * ps.c (options): Update doc for --top.
+ Add --head and --bottom/--tail/-b options.
+ (main): Implement --bottom/--tail/-b.
+
+Tue Dec 10 11:40:04 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * login.c (add_utmp_entry): ut_addr has changed to be an array;
+ stash the IP address in the first slot.
+
+Sat Nov 23 16:30:16 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * psout.c (psout): Delete var REMOVE.
+
+Fri Nov 15 19:05:46 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (read_utmp_procs): Function removed.
+ (main): Use utmp.h functions for reading utmp.
+
+Thu Oct 24 15:00:49 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (options): Remove header for run-time supplied fields.
+ (main): And add it back here as a child header.
+
+Wed Oct 23 14:04:47 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (main): Replace PARENTS with CHILDREN, which uses the
+ new argp_child structure.
+
+Thu Oct 10 13:53:23 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (options, main): Add --top/-h option.
+ * psout.c (psout): Add TOP parameter.
+ * psout.h (psout): Add TOP parameter.
+ * w.c (main): Supply new TOP arg to psout.
+
+Wed Oct 9 14:24:54 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (w_fetch): Use tty st_atime for idle time.
+
+ * w.c (_w_specs): Use ps_cmp_unames for `Name'.
+ Use ps_nominal_string for `Name' and `From'.
+ (ps_cmp_unames, ps_nominal_string): New declarations.
+ (DEFAULT_FMT_STRING): Add %pid.
+
+Tue Oct 8 14:44:04 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (print_store): Properly ignore STORE_INACTIVE.
+
+ * w.c (w_fetch): Set HAVE bits even if the values are `nominal'.
+ Correctly propagate PS->inapp bits.
+ (_w_specs): Add PS_FMT_FIELD_COLON_MOD to Idle's flags.
+ (main): Initialize dependencies field of USER_HOOKS to w_deps.
+ (w_deps): New function.
+
+ * storeinfo.c (print_store): Correctly print unknown flags.
+ Don't ever print STORE_INACTIVE flag.
+
+Mon Oct 7 15:48:23 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (main): Use STORE_INACTIVE flag to store_create.
+
+Thu Oct 3 16:08:21 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (name_to_option): Correct test for malloc failure.
+ (get_memobj_hit_ratio): Renamed from get_cache_hit_ratio.
+ (fields): Add `max' field.
+ Rename `cache *' fields to `memobj *', and reword doc strings.
+ (val_width): New function.
+ (main): Calculate verbose output widths differently.
+ (main: FWIDTH): New macro.
+ (VAL_MAX_MEM, VAL_MAX_SWAP, K, M, G): New macros.
+
+Wed Oct 2 10:24:04 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (struct field): Remove desc field, add doc field.
+ (fields): Initialize doc field, and not desc.
+ (name_to_option): New function.
+ (main): Rearrange use of name, doc, and desc fields in struct field.
+
+ * sush.sh: Handle `-' and `--' args properly.
+
+Fri Sep 27 13:01:31 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (options, main): Change --runs/-r to --block-list/-l.
+ (print_store): Print runs in grub block-list format.
+
+Fri Sep 27 01:07:20 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (options, main): Change --kind/-k to --type/-t.
+ Add --flags/-f option.
+ (W_TYPE): Renamed from W_KIND; all uses changed.
+ (W_FLAGS): New macro.
+ (print_store): Print flags separately from type.
+ (doc): Updated.
+
+Thu Sep 26 16:10:03 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (%: %.sh): Rule removed.
+
+Tue Sep 24 17:05:08 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main: verify_passwd): Only declare reference to crypt
+ weak if HAVE_CRYPT isn't defined.
+
+ * sush.sh: Fix option parsing.
+
+Mon Sep 23 00:19:05 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sush.sh, uptime.sh: Add option parsing, version number, &c.
+ Add copyright notice.
+
+ * Makefile (login-LDLIBS): Add $(LIBCRYPT).
+
+Thu Sep 12 16:37:42 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): New variable.
+
+Thu Sep 5 11:45:58 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile: Use $(top_srcdir)/sh-version.h to make executables
+ from .sh files.
+
+ * fsysopts.c: Include <version.h>.
+ (argp_program_version): Define with STANDARD_HURD_VERSION.
+ * w.c: Likewise.
+ * vmstat.c: Likewise.
+ * vminfo.c: Likewise.
+ * sync.c: Likewise.
+ * storeinfo.c: Likewise.
+ * showtrans.c: Likewise.
+ * settrans.c: Likewise.
+ * ps.c: Likewise.
+ * portinfo.c: Likewise.
+ * login.c: Likewise.
+ * hurdids.c: Likewise.
+
+Sun Sep 1 14:15:46 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sync.c (argp_program_version): New variable.
+ (main): Call argp_parse with just a doc string.
+ <argp.h>: New include.
+ * Makefile (sync): Depend on libshouldbeinlibc.a.
+
+Fri Aug 30 16:54:57 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vminfo.c: New file.
+ * Makefile (targets): Add vminfo.
+ (SRCS): Add vminfo.c.
+ (INSTALL-vminfo-ops): New variable.
+
+Mon Aug 19 15:19:20 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * devprobe.c, fsysopts.c, hurdids.c, login.c, portinfo.c, ps.c,
+ settrans.c, showtrans.c, storeinfo.c, vmstat.c, w.c, x.c (doc):
+ Add program description.
+
+Wed Aug 14 10:18:41 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (login-LDLIBS): New variable.
+ (login): Dependency on -lutil removed.
+ (libutil-libsubst): Variable removed.
+
+Sat Aug 10 10:03:57 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (options): Whoops, short option for --sort is -s.
+
+ * Makefile (LCLHDRS): New variable.
+
+Wed Jul 31 14:23:31 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main/verify_passwd): Use #pragma weak instead of
+ __attribute__ ((weak)).
+ * su.c (check_password): Likewise.
+
+ * login.c (options): Change --inherit-environ to --preserve-environment.
+ Change --no-environ to --no-environment-args.
+ Change --environ to --envvar and --environ-default to --envvar-default.
+ * ps.c (options, main): Change --fmt to --format/-F.
+ Change --posix-fmt/-o to --posix-format/-o.
+ Change --sort to --sort/-s.
+ Change --pgrp to --pgrp/-G.
+ Change --login to --login/-L.
+ Change --threads/-s to --threads/-T.
+ Change --session to --session/-S.
+ (OPT_FMT, OPT_SORT, OPT_PGRP, OPT_LOGIN, OPT_SESS): Macros removed.
+ * w.c (options, main): Change --fmt to --format/-F.
+ Change --sort to --sort/-s.
+ (OPT_FMT, OPT_SORT): Macros removed.
+
+Wed Jul 31 14:24:05 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * login.c (main/verify_passwd): Declare crypt weak in a portable
+ way.
+ * su.c (check_password): Likewise.
+
+Tue Jul 30 14:49:48 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * login.c (main/verify_passwd): If government is broken, don't use
+ crypt.
+ * su.c (check_password): Likewise.
+
+ * login.c (main/verify_passwd): Provide correct prototype for
+ crypt.
+ * su.c (check_password): Likewise.
+
+Mon Jul 29 03:22:07 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (val_t): Make `long long'.
+
+Sun Jul 28 21:13:31 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (main): Correctly parse the -w/--width option.
+
+Fri Jul 26 12:34:00 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (enum val_display_type): Add `PAGESZ'.
+ `SIZES' -> `SIZE'; all uses updated. Remove BYTES, PAGES, and KBYTES.
+ (fields): disp_type of "pagesize" and "swap-pagesize" changed to PAGESZ.
+ (val_display_type_is_size): Function removed.
+ (struct field): `disp_type' field -> `type'; all used updated.
+ (print_val): Add TYPE & SIZE_UNITS arguments; remove HOW & PSIZE.
+ (main): Variable user_disp_type removed. Variable size_units added.
+ Add SIZE_UNITS macro, remove FDISPTYPE macro.
+ Calls to print_val now use new PVAL macro.
+ (get_vmstats_field): Just test against a type of SIZE, instead of
+ using val_display_type_is_size.
+
+Thu Jul 25 22:36:38 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (INSTALL-vmstat-ops): New variable.
+
+ * vmstat.c (options, main): -k/--kilobytes, -v/--pages, and
+ -b/--bytes options added.
+ (fields): New struct members initialized. `size',
+ `cache-hit-ratio', `swap-size', `swap-active', `swap-free', and
+ `swap-pagesize' added.
+ (val_t, enum val_display_type, enum field_change_type): New types.
+ (val_display_type_is_size, print_val, vm_state_refresh,
+ vm_state_get_field, get_vmstats_field, get_size,
+ ensure_def_pager_info, get_swap_size, get_swap_free,
+ get_swap_page_size, get_swap_active): New functions.
+ (struct field): CHANGE_TYPE, DISP_TYPE, STANDARD, and COMPUTE
+ fields added. CUM field removed.
+ (struct vm_state): New type.
+ (main): Changed greatly to use vm_state type & functions, use
+ print_val, and support CONST display types nicely.
+ (argp_program_version): Version changed to 1.1.
+
+Sun Jul 21 03:00:10 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * fsysopts.c (main): Print an error for no args.
+ * portinfo.c (main): For no args, use argp_usage.
+ (options): Minor fixes.
+
+Sat Jul 20 15:54:10 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (DIST_FILES): Deleted var (which was `shd.ChangeLog').
+ (shd.ChangeLog): Deleted file.
+
+Fri Jul 19 21:09:57 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (options): Rearrange slightly.
+
+Tue Jul 16 21:38:01 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * login.c (main): If chown fails, print error message using errno,
+ not -1.
+
+Fri Jul 12 15:49:09 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (dog): Get rid of `Beware of dog' -- it can't ever happen.
+ Rename watch_login calls to check_login; don't use return value.
+ (check_login): Renamed from watch_login. Change type to void.
+ Exit if there's no such process.
+
+ * login.c (main): Only start a watchdog timer if in a new login coll.
+
+ * login.c (watch_login): New function.
+ (dog): Use watch_login. Get rid of wierd rules for root-gone-away.
+
+ * Makefile (INSTALL-ps-ops, INSTALL-w-ops): New variables.
+
+Fri Jul 12 14:20:44 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (INSTALL-login-ops): New variable.
+
+Sun Jul 7 21:31:36 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * settrans.c (main): Don't use unsafe MOVE_SEND in call to
+ file_set_translator.
+
+Sat Jul 6 18:06:52 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * portinfo.c, vmstat.c, hurdids.c, fsysopts.c, settrans.c,
+ showtrans.c, storeinfo.c, login.c, w.c, ps.c
+ (argp_program_version): New variable.
+ * vmstat.c <hurd.h>: New include.
+
+Fri Jul 5 22:28:11 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (add_utmp_entry): Pass the the basename of TTY to getutline.
+
+Wed Jul 3 14:00:08 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (add_utmp_entry): Don't fill in ut_line or ut_type
+ fields in UTMP.
+ Fill in the ut_addr field.
+
+ * Makefile (settrans): Depend on ../libports/libports.a.
+
+Tue Jul 2 14:54:43 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (dog): Include all args in the asprintf.
+ Put the ARGV message in parens.
+
+Mon Jul 1 13:05:53 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (kill_login): Don't kill dog.
+ (dog): Take new ARGV argument, and record status in it for ps.
+ (main): Pass ARGV to dog.
+
+ * w.c (_w_specs): Don't use utmp buffer sizes for field widths, as
+ they can be very large.
+
+ * login.c (add_utmp_entry): Always fill in UTMP.ut_line.
+ Set UTMP.ut_type.
+
+Fri Jun 28 15:44:15 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (add_utmp_entry): Increment TTY_FD while searching for a
+ TTY.
+
+Mon Jun 24 16:02:04 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Fix heuristic to decide whether native booted.
+
+Thu Jun 20 14:41:25 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * login.c (main): No need for EXEC_NEWTASK or EXEC_SECURE.
+
+ * Makefile (getty): Remove rule.
+ (targets): Remove `getty'.
+ (SRCS): Remove getty.c.
+ * getty.c: Removed file to daemons directory.
+
+Wed Jun 19 14:11:14 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (hurd_file_name_path_lookup): Declaration removed.
+ (main: child_lookup): Pass 0 for new LOOKUP arg to
+ hurd_file_name_path_lookup.
+
+Mon Jun 17 18:06:03 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (targets): Add `getty'.
+ (SRCS): Add `getty.c'.
+ (getty): Depend on -lutil.
+
+Mon Jun 17 10:41:28 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (targets): Remove reboot & halt.
+ (SRCS): Remove reboot.c & halt.c.
+ (login): Depend on -lutil instead of grot.
+
+Tue Jun 11 13:43:18 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (get_utmp_host): Function removed.
+ (add_utmp_entry): Get rid of TTY_FD parameter. Don't search for
+ the tty unless we need it to get the old host, since login does it
+ for us.
+ Also use the `existing host' in the case that a new one isn't specified.
+ (main): Update call to add_utmp_entry.
+
+ * login.c (dog): Don't print stupid message if login session is empty.
+ (add_utmp_entry): Use gettimeofday instead of time to get the time.
+
+Wed May 29 11:01:18 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sush.sh: Don't use -z flag to login.
+
+Tue May 28 17:48:12 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Fetch the parent uids before checking their number.
+ (dog): Don't kill session if the user logged in!
+ Print newline before message.
+ (check_owned): Return OWNED, not NOTOWNED.
+
+ * ps.c (fmts): Use %sz for vmem format, not %size.
+ (spec_abbrevs): Still use `NTH' for field name, just `TH' for title.
+ (fmts): Use %nth.
+
+Tue May 21 12:18:38 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (print_store): Get rid of class-name printing code,
+ and use store->class->name. Print all flags.
+
+Tue May 14 09:50:21 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (dog): Use error to print messages. Use pretty time fmting.
+ (main): Fork login timeout watchdog before clearing the process owner.
+
+ * login.c (main): Only allow real users to make new login collections.
+
+Mon May 13 18:10:43 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (options, main): Remove -z/--no-utmp option.
+ (main): Only add utmp entry for session leader.
+ Clear process owner if no uids.
+ Fork self-destruct timer when appropiate.
+ (default_args): Add NOAUTH_TIMEOUT entry.
+ (check_owned, kill_login, dog): New functions.
+
+Sun May 12 13:38:34 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * psout.c (psout): When printing result of ps_fmt_creation_error,
+ don't pass ERR to error (it should already be in PROBLEM if necessary).
+
+Sat May 11 01:00:39 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (_parse_strlist, parse_strlist, parse_numlist, lookup_user,
+ main): Slather on consts, in a misguided attempt to shut up the
+ compiler.
+
+Fri May 10 13:53:30 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * devprobe.c (parse_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL.
+ * showtrans.c (parse_opt): Likewise.
+ * portinfo.c (parse_opt): Likewise.
+ * ps.c (parse_opt): Likewise.
+ * settrans.c (parse_opt): Likewise.
+ * login.c (parse_opt): Likewise.
+ * hurdids.c (parse_opt): Likewise.
+ * fsysopts.c (parse_opt): Likewise.
+
+ * showtrans.c (parse_opt): Print a usage msg with no args.
+ (options, main): Add --translated/-t option.
+
+ * Makefile (storeinfo): Depend on ../libstore/libstore.a.
+
+ * settrans.c (main): Remove const cast from first arg to argz_create.
+ * fsysopts.c (main): Likewise.
+ * login.c (main): Likewise.
+ (main): Remove const from decl of USER & ARG.
+ (fail): Remove const from decl of VIA.
+
+ * ps.c (parse_strlist): Make DEFAULT_FN return a const char *.
+
+ * storeinfo.c (print_store): New function.
+ (info): Use store_create to make a store and print_store to print it.
+ (parse_opt): Print a usage message for no args.
+ (options, parse_opt): Add --children option.
+
+Thu May 9 19:46:14 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * w.c (uptime): Cast arg to localtime appropriately.
+
+ * login.c (add_utmp_entry): Declare HOST `char const *'.
+ (main) [fail]: Declare VIA `char const *'.
+ (main): Declare USER `char const *'.
+ Declare ARG `char const *'.
+
+ * login.c (main): Provide new third arg to proc_setowner.
+
+ * fsysopts.c (main) [parse_opt]: Cast first arg of argz_create
+ appropriately.
+ * settrans.c (main) [parse_opt]: Likewise.
+ * login.c (main) [parse_opt] [case ARGP_KEY_ARG]: Likewise.
+ (main): Likewise.
+
+ * ps.c (main) [current_tty_name]: Remove `const' keyword to avoid
+ type clash.
+
+ * ps.c (main) [proc_stat_has_ctty]: Add parentheses around
+ assignment used as truth value.
+
+Mon May 6 17:36:22 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * psout.c (psout): Take a field, not a spec. Honor PS_FMT_FIELD_KEEP.
+
+ * w.c (w_user_getter): New function.
+ (w_get_user): New function.
+ (w_fetch): Implement W_PSTAT_USER.
+ (struct w_hook): Add user field.
+ (_w_specs): Add "Name" entry.
+ (w_get_uname): Renamed from w_get_user.
+ (w_uname_getter): Renamed from w_user_getter.
+ * ps.c (fmts): Upcase most format strings.
+
+Sun May 5 01:05:54 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * psout.h: New file.
+ * ps.c (fmts): Use new field syntax.
+ (options): Add -o/--posix-fmt option. -A is an alias.
+ "psout.h": New include.
+ * w.c (_w_specs): Add precision & keep fields.
+ (DEFAULT_FMT_STRING): Use new field syntax.
+ "psout.h": New include.
+ (main): Update call to psout.
+ * psout.c (psout): Add posix_fmt arg.
+
+Thu May 2 00:10:53 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * psout.c (psout): Use ps_fmt_creation_error to find out in detail
+ why ps_fmt_create fails.
+
+Wed May 1 19:53:51 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (main): Default KILL_ACTIVE to 0.
+
+Tue Apr 30 19:04:01 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (_w_specs): Use ps_emit_past_time to show login times.
+
+Tue Apr 30 09:47:03 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (all): Remove target.
+ (include ../Makeconf): Place before all dependencies.
+ ($(targets)): Each separate target should depend on its own .o.
+
+Tue Apr 23 13:49:36 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Try to intuit whether this is a native-booted hurd
+ system, and if so, don't filtered out non-parented processes.
+
+Wed Apr 10 19:47:45 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * devprobe.c: New file.
+ * Makefile (targets): Add devprobe.
+ (SRCS): Add devprobe.c.
+
+Mon Apr 8 17:09:55 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * portinfo.c (options, doc): Change meaning of --translate's
+ argument, in preparation for other changes.
+ (options, main): Add, but don't really implement, --search option.
+
+Fri Mar 29 14:37:49 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (options): Add --exclusive option, change descriptions.
+ (main): Rearrange meanings of arguments somewhat.
+
+Thu Mar 28 13:58:36 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * portinfo.c (main): Use new names for functions, and pass stdout.
+ (port_info, ports_info, xlated_port_info, xlated_ports_info,
+ name_xlator_create, name_xlator_free, name_xlator_xlate):
+ Functions removed (renamed and put in libshouldbeinlibc).
+
+ * portinfo.c (parse_task): Use strtoul instead of atoi so pid 0 works.
+
+Mon Mar 25 14:19:40 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (uptime): Correctly calculate uptime.
+ (...various...): Use real types instead of old ps typedefs.
+ * ps.c (spec_abbrevs): Make const.
+ (ps_specs): Make non-const.
+ (...various...): Use real types instead of old ps typedefs.
+ * psout.c (psout): Use real types instead of old ps typedefs.
+
+Sat Mar 23 01:02:37 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (uptime): Add a temporary hack to pretend to show uptime.
+
+Mon Mar 18 18:34:51 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Pass new arg to argp_parse.
+ * w.c (main): Likewise.
+ * storeinfo.c (main): Likewise.
+ * fsysopts.c (main): Likewise.
+ * hurdids.c (main): Likewise.
+ * login.c (main): Likewise.
+ * vmstat.c (main): Likewise.
+ * showtrans.c (main): Likewise.
+ * settrans.c (main): Likewise. Also use argp_usage correctly.
+ * portinfo.c (main): Likewise.
+
+Tue Mar 5 14:17:22 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * portinfo.c (main): Fix arg order to name_xlator_create.
+ (name_xlator_create): Return X in XLATOR.
+ (name_xlator_free): Deallocate all ports in X too.
+ (main): Use xlated_port_info / xlated_ports_info.
+ (xlated_port_info, xlated_ports_info): New functions.
+ (struct name_xlator, name_xlator_create, name_xlator_xlate): Don't
+ depend on a specified receive/send type for the source being specified.
+ (name_xlator_xlate): Take FROM_TYPE arg, & return TO_TYPE.
+ (options, main): Get rid of -R/-S options, and add --translate/-t
+ option.
+
+Mon Mar 4 15:25:41 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * portinfo.c (struct name_xlator): New structure.
+ (name_xlator_create, name_xlator_free, name_xlator_xlate): New funcs.
+ (options, main): Add --translate-{receive,send}/-R/-S options.
+
+Fri Mar 1 18:55:07 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (targets, SRCS): Add portinfo & portinfo.c.
+
+Tue Feb 27 14:51:16 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (targets, SRCS): Remove clri & clri.c.
+
+Mon Feb 26 13:50:38 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (main): Don't print a newline after `Pausing...' msg.
+
+ * ps.c: Include <error.h> instead of "error.h".
+ "common.h": Don't include this anymore.
+
+Wed Feb 21 11:47:01 1996 Roland McGrath <roland@charlie-brown.gnu.ai.mit.edu>
+
+ * w.c (uptime): Use %.2f format for load average numbers.
+
+Mon Feb 19 15:49:46 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (STRINGIFY): Make work. Is this in a header somewhere?
+ (_STRINGIFY): New macro. Ick.
+
+ * fsysopts.c (main): Use ARGP_IN_ORDER.
+
+Sat Feb 17 23:47:36 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * uptime.sh: New file.
+ * Makefile (targets, special-targets): Add uptime.
+ (SRCS): Add uptime.sh.
+
+Thu Feb 15 15:47:45 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Don't make -a imply -g.
+
+ * sush.sh: Change -aBACKUP_SHELL to -aBACKUP_SHELLS.
+
+ * login.c (child_lookup): Pass last arg to hurd_file_name_path_lookup.
+
+Wed Feb 14 17:38:31 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * w.c (add_utmp_procs): Correctly add terminal processes using new
+ libps functionality.
+ (read_utmp_procs): Emit all utmp entries, even the last one.
+
+ * ps.c (main): Get rid of totally dead processes/threads.
+ (add_pid): Complain about non-existant processes.
+ (psout): New declaration.
+
+ * ps.c (parse_numlist): Correctly handle NULL hook functions.
+ (parse_opt): Deref STATE correctly.
+ (main): Update calls to changed proc_stat_list_add_* functions.
+
+ * login.c (main): Don't set old SAW_USER_ARG variable.
+
+Tue Feb 13 13:54:03 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sush.sh: Exec login.
+
+ * login.c (main): Deal with the shell args reasonably.
+
+ * ps.c (parse_opt): Don't turn quoted args into options.
+
+Mon Feb 12 14:54:38 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (options, main): Add --timeout/-t option.
+ (main): Pass ARGP_IN_ORDER to argp_parse (it's no longer the
+ default), and deal with the fallout.
+
+Wed Feb 7 23:11:10 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Allow switches in the login args naturally.
+
+Mon Feb 5 14:18:03 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (main, args_doc, doc): Add repeat mode.
+ (FVAL): New macro.
+
+ * vmstat.c (main): Slightly decrease the space for numbers in the
+ verbose output format.
+ Twiddle the widths of the terse fields to make sure there's room
+ for typical numbers.
+
+Sat Feb 3 01:28:20 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * vmstat.c (fields, options): Make const.
+
+ * vmstat.c: New file.
+ * Makefile (targets): Add vmstat.
+ (SRCS): Add vmstat.c
+
+ * login.c (main): Correctly add gids.
+ Avoid duplicating the old real id when keeping the old ids.
+ Don't ask for a password unnecessarily.
+
+Thu Feb 1 19:15:53 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sush.sh: Don't save the parent ids.
+
+ * login.c (main): When the user is specified as the first
+ argument, always add it as both effective, real, and saved ids,
+ even if there are others already.
+
+ * login.c (main): Get the ttyname to chown.
+ Only do the chown if NO_LOGIN isn't set.
+
+ * settrans.c (options, main): Add --pause option.
+
+Thu Feb 1 16:27:24 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * login.c (main): Chown the terminal.
+
+Tue Jan 30 15:25:23 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Put LOGNAME in the environ even if it was already.
+ (copied_args): Add "USER".
+
+Mon Jan 29 09:52:08 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (add_utmp_entry): Get rid of declaration for login().
+ (main, default_args): Replace the BACKUP_SHELL param with
+ BACKUP_SHELLS, which is a list of things to try. The default
+ includes both bash and the /bin/sh.
+ (main): Try to set the default path using confstr().
+
+Mon Jan 15 12:29:49 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (options, main): Make -g/--goaway only apply to
+ active translators.
+ (options): Rearrange a bit.
+
+Sun Jan 14 12:45:40 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Call setlogin().
+
+Thu Jan 11 19:30:27 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (options, main): Change the -R/--retry option so that
+ the optional argument is an argument to add to those passed to
+ login when retrying, and remove all the hardwired arguments
+ (except propagating -h/--via).
+ (default_args, options, main): Get rid of the -n/--nobody option
+ and variables, making it an additional login parameter.
+
+ * login.c (default_args): Make the default shell /bin/bash instead of
+ _PATH_BSHELL.
+ (default_args, default_env): Make the default path just /bin.
+
+ * loginpr.sh (user): Pass the appropiate -R options to login.
+
+Wed Jan 10 15:32:19 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Turn on RETRY when the -h option is specified.
+
+Fri Jan 5 15:21:36 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (add_canonical_host): Try harder to get the actual host name.
+
+Thu Jan 4 22:37:46 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (add_canonical_host): New function.
+ (add_utmp_entry): Use VIA_ADDR instead of VIA in the utmp entry if
+ it fits better.
+ (add_entry): Function moved out of main.
+ (main): Use add_canonical_host() to implement the -h option. Only
+ let root specify the login host.
+
+ * login.c (main): child_lookup() now takes an additional PATH arg,
+ and calls hurd_file_name_path_lookup() instead. Pass a path when
+ looking up the shell executable.
+
+Tue Jan 2 01:15:13 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * sush.sh: Instead of giving --su to login, use all the component
+ options it stood for. Also use --program-name for nice errors.
+
+ * login.c (main): Do all file-name lookups using the target
+ authentication, so that login isn't a security hole.
+ Rework how password-checking is done somewhat (only ask when necess).
+ Call exec_reauth() with SECURE==0 so that it reauths all the ports.
+ If setting the real [ug]id, also add it as the `saved id'.
+ (cat): Take an io_t port instead of a file descriptor.
+ (options, main): Get rid of the -s/--su option, and add -S/--shell-arg.
+ (FAILURE_SHELL): Macro deleted.
+ (default_args): Add BACKUP_SHELL param.
+ (main): Use BACKUP_SHELL instead of FAILURE_SHELL define.
+
+Mon Jan 1 20:51:41 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main): Use exec_reauth() instead of our own.
+ (options, main): Add -k/--keep & -s/--su options.
+ (options, main): Remove -m/--umask option; use UMASK param instead.
+ (main): Get rid of various string vars only used once.
+
+Fri Dec 29 12:16:13 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * loginpr.sh: New program.
+ * login.c (main): Add optional shell argument for --retry.
+ Add --paranoid/-P option.
+
+ * login.c (main): Don't ask for password by name if only one user.
+
+Thu Dec 28 17:41:11 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c (main, options): Add --retry/-R option.
+
+ * login.c (main): If -f/--no-passwd is specified, get rid of the
+ effect of the login executable being setuid/gid.
+ Only set the shell proc's owner to an effective uid.
+
+Sun Dec 24 14:26:18 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Move a bunch of stuff into psout() in psout.c.
+ * psout.c: New file.
+
+Sat Dec 23 20:49:26 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Allow user to use `-' prefix to invert sort as well.
+ (main): Use ps_fmt_set_output_width() to set the output width.
+
+ * login.c (add_utmp_entry): Only remove a prefix from the tty name
+ if it's _PATH_DEV.
+
+Thu Dec 21 11:15:42 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (spec_abbrevs, ps_specs): New variables.
+ (fmts): Get rid of redundant header specifications.
+ (fmt_sortrevs): Variable removed.
+ (main): Don't allow sorting on field names any more, just spec
+ names (prob ok, since most things ps prints are now spec names).
+ (main, options): Rename -o flag to -U for posix compat (ick).
+
+ * login.c (options): Add --no-utmp/-z option.
+ (add_utmp_entry): New function.
+ (main): Call add_utmp_entry().
+ (main): Ivec routines are now idvec routines.
+ Include <idvec.h> instead of <ivec.h>.
+ (options): --host/-h is now --via/-h.
+ (main): Store the host were logged in from via in VIA, in the
+ login parameters instead of the child environment; optionally copy
+ it into the environment too.
+ (main): Enable EXEC_SECURE.
+ * Makefile (libutil-libsubst): New hack.
+ (login): Depend on -lutil.
+
+ * ps.c (main): Ivec routines are now idvec routines.
+ Include <idvec.h> instead of <ivec.h>.
+ Use argz functions to store tty_names instead of our own.
+
+Sun Dec 17 00:24:26 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main, options): If the specified sort field begins with
+ `-', reverse the sort.
+
+ * ps.c (main): If there is no current uid, don't try to filter with it.
+ (FILTER_NOT_LEADER): Renamed from FILTER_NSESSLDR.
+ (main): Rename ps_not_leader_filter from ps_not_sess_leader_filter.
+
+ * ps.c (main): Use ivec routines instead of ids.
+ (make_ids, ids_add, ids_contains): Routines deleted.
+ Include <ivec.h>.
+
+Sat Dec 16 22:13:32 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (options): Reformat doc string for --tty option.
+
+ * ps.c (options): Add argument and doc for 'w' option.
+ (main): Implement 'w' option.
+
+ * login.c: Zillions of changes.
+
+Tue Dec 12 20:16:22 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * login.c: New file.
+ * Makefile (SRCS): Add login.c
+ (targets): Add login.
+ (login): Depends on ../libshouldbelibc/libshouldbelibc.
+
+Wed Dec 6 15:12:15 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * fsysopts.c (main): Supply the new SEP argument to argz_stringify.
+ * showtrans.c (main): Ditto.
+
+ * fsysopts.c (main): Change uses of the INDEX field in argp_state
+ structures to use NEXT instead.
+ * ps.c (main): Ditto.
+ * settrans.c (main): Ditto.
+ * showtrans.c (main): Ditto. Fix default prefix-printing test.
+ * storeinfo.c (main): Ditto.
+
+Mon Dec 4 15:41:06 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (main): Correctly whether to print prefix by default.
+ (options): Fix help strings.
+
+Wed Nov 15 19:56:21 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Use new libps stream type.
+
+Tue Nov 14 18:28:20 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Grow TTY_NAMES properly even when it's empty.
+
+Mon Nov 6 12:41:21 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (ids_add): Increase the size of IDS even when 0.
+
+ * fsysopts.c (main): Use file_get_fs_options, not fsys_get_options.
+ (doc): `filesystem' --> `FILESYS'.
+
+Sat Nov 4 19:56:38 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (print_info): Use the new FLAGS argument to
+ file_get_storage_info. Add new storage types.
+
+Wed Nov 1 19:30:42 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (main): Change to use the new wierd callback
+ interface to fshelp_start_translator.
+
+ * ps.c (options): Add --not-owner/-O option.
+ (id_t, struct ids): New type.
+ (make_ids, ids_add, ids_contains): New functions.
+ (main): Use a struct ids instead of multiple variables to hold the
+ wanted uids list, which renamed to ONLY_UIDS. Add the NOT_UIDS
+ list to contain the opposite sense, and use it.
+
+ * ps.c (main): Use proc_stat_proc_info instead of proc_stat_info and
+ PSTAT_PROC_INFO instead of PSTAT_INFO.
+
+Tue Oct 31 17:57:25 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * fsysopts.c (doc, args_doc): Mention usage without any options.
+ (main): If no options are supplied, print existing options.
+
+ * ps.c (fmts): Add RPC field to the -l format.
+
+Mon Oct 30 16:24:37 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (options): --all is -e, not -a.
+
+Mon Oct 23 15:17:42 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (options): Change --force/-f to --goaway/-g. Add
+ flags for killing translators: --recurse/-r, --force/-f, --nosync/-S.
+ (doc): New variable.
+ (main): Support new flags. Have some of the options update flag
+ words instead of setting variables.
+
+ * storeinfo.c (print_info): Calculate total size/blocks correctly.
+
+Fri Oct 20 15:44:45 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * settrans.c (args_doc): New variable.
+ (main): Set ARGZ inside of parse_opt.
+
+ * fsysopts.c (options): FILE --> FILESYS in help msg.
+ (args_doc): OPTION --> FS_OPTION, since the usage message already
+ uses OPTION to mean those to fsysopts.
+ (doc): New variable; give some common choices for FS_OPTION.
+
+Thu Oct 19 19:07:26 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (parse_enum): Use argp_error.
+
+Thu Oct 12 15:22:24 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (parse_enum): Use ARGP_HELP_STD_ERR.
+
+Wed Oct 11 19:03:19 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ps.c (main): Use realloc on a variable in main instead of
+ alloca, since the storage gets used after parse_opt returns.
+
+ * fsysopts.c (main): Use argp_help instead of argp_usage.
+
+Tue Oct 10 15:02:17 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * showtrans.c (options): Converted to argp format.
+ (main): Use argp, not getopt.
+ (usage, USAGE, SHORT_OPTIONS): Deleted.
+ (arg_doc, doc): New variables.
+ Include <argp.h> not <getopt.h>.
+ * fsysopts.c (options): Converted to argp format.
+ (main): Use argp, not getopt.
+ (usage, USAGE, SHORT_OPTIONS): Deleted.
+ (arg_doc): New variable.
+ Include <argp.h> not <getopt.h>.
+ * settrans.c (options): Converted to argp format.
+ (main): Use argp, not getopt.
+ (usage, USAGE, SHORT_OPTIONS): Deleted.
+ Include <argp.h> not <getopt.h>.
+ * ps.c (options): Converted to argp format.
+ (main): Use argp, not getopt.
+ (usage, USAGE, SHORT_OPTIONS): Deleted.
+ (arg_doc, doc): New variables.
+ Include <argp.h> not <getopt.h>.
+
+Fri Oct 6 17:33:01 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * storeinfo.c (print_info): Use the new block_size value returned
+ from file_get_storage_info.
+
+Wed Oct 4 18:15:59 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * showtrans.c (usage, options, main): Change -h/--no-header options to
+ -p/--prefix and -P/--no-prefix.
+ (main): Unless overridden by -p/-P, only print a FILE: prefix when
+ there are multiple files on the command line.
+
+Mon Oct 2 19:00:27 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (targets): Add storeinfo.
+ (SRCS): Add storeinfo.c.
+
+Fri Sep 1 11:35:12 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * settrans.c (main): Use fshelp_start_translator instead of
+ start_translator from ../lib.
+ * Makefile: Get rid of rules related to ../lib.
+ (settrans): Depend on libfshelp.a instead of starttrans.o.
+
+Thu Aug 24 10:44:17 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile (all): New target.
+ (ps, settrans, showtrans, fsysopts): Put all dependencies in these
+ targets.
+ (settrans-HURDLIBS, showtrans-HURDLIBS, ps-HURDLIBS): Removed.
+
+Tue Aug 22 17:56:09 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile (settrans-HURDLIBS, showtrans-HURDLIBS, ps-HURDLIBS):
+ Add libshouldbeinlibc.
+ (settrans, showtrans, fsysopts, ps): Get rid of things that are
+ now in libshouldbeinlibc.
+ Get rid of rules dealing with error.o.
+
+Sun Jul 23 15:58:06 1995 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * Makefile (DIST_FILES): sh.ChangeLog -> shd.ChangeLog.
+
+Fri Jul 7 19:20:21 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * clri.c (copyright, sccsid): Correct syntax.
+
+Fri Jul 7 18:56:45 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * settrans.c (SHORT_OPTIONS): Remove '?' as getopt usurps it.
+ (options, main): Use '&' instead of '?' to mean help.
+
+Thu Jul 6 21:04:04 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * su.c (main, apply_auth_to_loginid, apply_auth_to_pgrp): Remove
+ assignments from inside if tests.
+
+ * sync.c (main): Declare return type.
+
+ * clri.c (copyright, sccsid): Declare unused.
+ (main): Correct format for fourth arg in printf call.
+
+ * ps.c (lookup_user): Declare return to be `int' to avoid type
+ clash in use as 4th parameter to parse_numlist.
+
+ * shd.c (run): Remove assignments from inside if tests.
+ (command): Likewise.
+
+ * Makefile (special-targets): New var.
+ (mount): Provide command.
+ (OBJS): New var.
+ (shd, su, clri, sync, reboot, halt): List object files here.
+
+Thu Jul 6 16:12:22 1995 Miles Bader <miles@geech.gnu.ai.mit.edu>
+
+ * settrans.c (main): Give an error message instead of dying when
+ no filename argument is given.
+
+Thu Jul 6 15:43:15 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile: Removed dependencies that are now automatically
+ generated.
+
+Wed Jul 5 21:18:42 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile (ps-HURDLIBS): Renamed from HURDLIBS.
+ (ps): Fix dependencies.
+
+Mon Jun 26 16:13:47 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile (CPPFLAGS): Put -I../lib back in.
+ Put back the vpath for %.c to ../lib.
+
+Tue Jun 6 13:22:52 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): Added libihash.
+ (CPPFLAGS): Deleted addition of -I../lib.
+ (vpath): Deleted spec.
+ (ps): Deleted ihash.o and primes.o.
+
+Thu Jun 1 11:33:47 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile (ps.o, fsysopts.o, showtrans.o, settrans.o): Add
+ dependencies on header files.
+ (REMHDRS): Define this variable.
+
+Tue May 30 12:17:33 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile (SCRS): Removed update.c.
+ (targets): Removed update.
+
+Sat May 20 00:51:50 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * fsysopts.c (main): Check for a missing filesystem name.
+ (main): Tweak the error msgs a bit.
+
+ * fsysopts.c (options, main): Don't use '?' as the --help key.
+
+Mon May 15 20:31:31 1995 Miles Bader <miles@duality.gnu.ai.mit.edu>
+
+ * Makefile (SRCS, targets): Add fsysopts.
+
+Wed May 3 11:37:39 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * ps.c (main, usage, SHORT_OPTIONS, options): Add the -n flag
+ (--nominal-fields), which prevents elision of uninteresting fields.
+ Also add elision of uninteresting fields...
+
+Tue May 2 17:22:11 1995 Miles Bader <miles@geech.gnu.ai.mit.edu>
+
+ * ps.c (fmts): Add the SC (suspend count) field to various output fmts.
+ (usage, main, SHORT_OPTIONS): Add the (ignored) -w flag for BSD compat.
+ (main): Use the new name for ps_fmt_squash: ps_fmt_squash_flags.
+
+Tue May 2 14:37:07 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * shd.c (reap): Check for ECHILD, not ESRCH.
+
+Wed Apr 26 21:40:57 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * settrans.c (main): Allow options before and immediately after
+ the node name to be rearranged by getopt without affecting those
+ following the translator name.
+
+Fri Apr 14 10:18:34 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * mount.sh: Move the test for a valid translator to just before we
+ use it. Gratuitously change the "unknown" value for type to "".
+ Don't use `--' when invoking settrans, as getopt doesn't seem to
+ be handling it correctly. Use `usage' not `USAGE'.
+
+Wed Apr 12 14:38:25 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile (DIST_FILES): Omit ps.ChangeLog.
+
+Tue Apr 11 15:21:46 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile (targets): Changed `sh' to `shd'.
+ (SRCS): Changed `sh.c' to `shd.c'.
+ * shd.c: Renamed from `sh.c'.
+
+Mon Apr 10 20:01:20 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * ps.c (main): Tweak things so that the -t flag works correctly
+ for processes whose tty we can't figure out.
+
+Sun Apr 9 14:00:09 1995 Miles Bader <miles@duality.gnu.ai.mit.edu>
+
+ * su.c (main): Getopt now returns '\001' for non-switch options
+ instead of '\0', no doubt to work around an obscure bug in some
+ brain-dead system only used by 2 people twice a decade.
+
+Fri Apr 7 11:55:25 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * ps.c (parse_enum): Renamed from enum_name.
+ (main, usage): Add lots of sysvish options, and generalize some
+ existing ones to deal with sysvish usage. Make -t & -o work.
+
+Thu Apr 6 11:56:23 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * ps.c (main): Add the `jobc' (-j) output format from netbsd.
+
+Wed Apr 5 22:10:50 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * ps.c (main): When processes are specified on the command line by PID,
+ turn off all filtering so that they don't disappear later. Also
+ minor changes in some spec names.
+
+Tue Apr 4 11:32:53 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * Makefile: Add showtrans & mount, and uncomment-out su.
+
+ * settrans.c (main): Get rid of the `show passive translator'
+ functionality, as this is now done by showtrans.
+
+ * ps.c (main): Make -a imply -g as well, to be compatible with bsd.
+ Add the -M (--no-msg-port) switch, which disables all fields that
+ would use any process's message port.
+
+ * showtrans.c: New file: Show passive translators.
+
+ * su.c (apply_auth): Use msg_add_auth & msg_del_auth instead of
+ add_auth & del_auth.
+ (apply_auth_to_pids, apply_auth_to_loginid, apply_auth_to_pgrp):
+ Don't use the IDS variable, it's no longer around; I think AUTH is
+ the right thing to replace it with.
+
+Tue Apr 4 01:47:57 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * ps.c (main): Treat argument without leading - as if it had one,
+ unless it's a number.
+
+Mon Mar 20 20:17:04 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu>
+
+ * Makefile (ps): Link in ihash.o, error.o, and primes.o too.
+ * Makefile (vpath %.c): New decl.
+
+ * ps.c: Include <hurd/ps.h> instead of "ps.h".
+ Include <unistd.h>.
+ (enum_name): Avoid warning.
+ (program_name): New variable.
+ (main): Don't set program_invocation_short_name (the library does it
+ for us). Do set program_name.
+
+ * ps.c: Replace with new version by Miles Bader. See ps.ChangeLog
+ for some changes made to the new version before the replacement.
+ Old ps.c and ps.ChangeLog are now ps.c.old and ps.ChangeLog.old.
+ * Makefile (HURDLIBS): Define, for ps.
+ (CPPFLAGS): Define, for ps.
+ (ps): Add rule to get library.
+
+Sat Jan 28 15:02:08 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * sh.c (main): Only open /dev/tty if stdin is unreadable.
+
+Wed Nov 16 20:28:40 1994 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * reboot.c: Include unistd.h, stdio.h, not hurd/anything.
+ (main): If reboot returns, give error message and return 1.
+ * halt.c: Likewise.
+
+Sat Nov 12 21:20:07 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * reboot.c (main): Just use the reboot function.
+ * halt.c (main): Likewise.
+
+Fri Nov 11 12:05:38 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * Makefile (targets): Added reboot and halt.
+ (SRCS): Added reboot.c and halt.c.
+ * reboot.c: New file
+ * halt.c: New file.
+
+ * ps.c (main): Print in shorter format by default; take -v flag to
+ print in longer format.
+
+Wed Nov 9 04:43:54 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * ps.c (time_str): Specify format for decimals correctly.
+
+Wed Nov 9 00:20:09 1994 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * ps.c (time_str): Use %.2d instead of %2d to get 0 pads.
+
+Mon Nov 7 14:15:10 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * ps.c (time_str): Don't use floating point conversion;
+ it's buggy.
+
+Wed Nov 2 13:34:56 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * sync.c: New file.
+ * Makefile (targets): Added sync.
+ (SRCS): Added sync.c.
+
+Thu Oct 27 22:19:29 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * ps.c (main): Also print out a field with the number of threads.
+
+Tue Oct 4 19:40:22 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * clri.c: New file.
+ * Makefile (targets): Added clri.
+ (SRCS): Added clri.c.
+
+Sat Oct 1 03:44:55 1994 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * update.c: Take optional arg to specify sleep time.
+ Use daemon instead of doing its work by hand.
+
+Fri Sep 30 11:53:53 1994 Michael I Bushnell <mib@churchy.gnu.ai.mit.edu>
+
+ * update.c: New file.
+ * Makefile (SRCS): Added update.c.
+ (targets): Added update.
+
+Sat Sep 10 08:22:34 1994 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * settrans.c (main): Give usage message when given no args.
+ Use O_NOTRANS in file name lookup.
+ Don't use FS_TRANS_EXCL in file_set_translator.
+ If given args "FILE -show", get translator and print it out.
+
+Thu Sep 1 11:50:51 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * settrans.c: New file.
+ * Makefile (SRCS): Added settrans.c.
+ (targets): Added settrans.
+
+Tue Aug 30 16:40:54 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * sh.c (run): Use file_name_lookup instead of path_lookup.
+
+Tue Aug 23 10:44:16 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * Makefile (targets): Comment out `su' until Roland gets it back
+ into a usable state.
+
+Mon Aug 22 12:18:09 1994 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * sh.c (main): Open /dev/tty with O_IGNORE_CTTY and fdopen that
+ onto stdin/out/err.
+
+Fri Jul 22 12:26:27 1994 Michael I Bushnell <mib@geech.gnu.ai.mit.edu>
+
+ * Makefile: Rewritten to use new scheme.
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644
index 00000000..fd8035be
--- /dev/null
+++ b/utils/Makefile
@@ -0,0 +1,80 @@
+#
+# Copyright (C) 1994,95,96,97,98,99 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.
+
+dir := utils
+makemode := utilities
+
+targets = shd ps settrans showtrans syncfs su fsysopts \
+ storeinfo login w uptime ids loginpr sush vmstat portinfo \
+ devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \
+ storeread ping msgport rpctrace mount
+special-targets = loginpr sush uptime
+SRCS = shd.c ps.c su.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
+ fsysopts.c storeinfo.c login.c loginpr.sh sush.sh w.c \
+ uptime.sh psout.c ids.c vmstat.c portinfo.c devprobe.c vminfo.c \
+ parse.c frobauth.c frobauth-mod.c setauth.c pids.c nonsugid.c \
+ unsu.c ftpcp.c ftpdir.c storeread.c storecat.c ping.c msgport.c \
+ rpctrace.c mount.c
+LCLHDRS = psout.h parse.h pids.h frobauth.h
+
+OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
+HURDLIBS = ps ihash store fshelp ports threads ftpconn shouldbeinlibc
+login-LDLIBS = -lutil $(LIBCRYPT)
+addauth-LDLIBS = $(LIBCRYPT)
+setauth-LDLIBS = $(LIBCRYPT)
+su-LDLIBS = $(LIBCRYPT)
+
+ping-CPPFLAGS = -I$(srcdir)/../pfinet/
+
+INSTALL-login-ops = -o root -m 4755
+INSTALL-ids-ops = -o root -m 4755
+INSTALL-ps-ops = -o root -m 4755
+INSTALL-w-ops = -o root -m 4755
+INSTALL-vmstat-ops = -o root -m 4755
+INSTALL-vminfo-ops = -o root -m 4755
+INSTALL-ping-ops = -o root -m 4755
+
+include ../Makeconf
+
+freeauth ps addauth rmauth setauth su unsu msgport: parse.o pids.o
+login addauth setauth su: nonsugid.o
+freeauth addauth rmauth setauth su unsu: frobauth.o
+rmauth setauth su unsu: frobauth-mod.o
+ps w: psout.o ../libps/libps.a ../libihash/libihash.a
+
+storeinfo storecat storeread: ../libstore/libstore.a
+ftpcp ftpdir: ../libftpconn/libftpconn.a
+
+ping: ../libthreads/libthreads.a
+
+# We must include libthreads because of a bug in the way shared libraries
+# work: all libraries that *any* routine in libfshelp uses must be defined.
+settrans: ../libfshelp/libfshelp.a ../libports/libports.a ../libthreads/libthreads.a
+ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \
+ devprobe vminfo addauth freeauth rmauth setauth su unsu ftpcp ftpdir storeread \
+ storecat msgport mount: \
+ ../libshouldbeinlibc/libshouldbeinlibc.a
+
+$(filter-out $(special-targets), $(targets)): %: %.o
+
+rpctrace: ../libports/libports.a ../libihash/libihash.a ../libthreads/libthreads.a
+
+mount: ../sutils/fstab.o ../sutils/clookup.o \
+ $(foreach L,fshelp ports,../lib$L/lib$L.a)
+../sutils/fstab.o ../sutils/clookup.o: FORCE
+ $(MAKE) -C $(@D) $(@F)
+FORCE:
diff --git a/utils/addauth.c b/utils/addauth.c
new file mode 100644
index 00000000..0932a33f
--- /dev/null
+++ b/utils/addauth.c
@@ -0,0 +1,100 @@
+/* Add authentication to selected processes
+
+ Copyright (C) 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 <string.h>
+#include <unistd.h>
+#include <argp.h>
+#include <idvec.h>
+#include <ugids.h>
+#include <error.h>
+#include <hurd.h>
+#include <version.h>
+
+#include "frobauth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (addauth);
+
+static struct argp_child child_argps[] = {{ &frobauth_ea_argp }, { 0 }};
+
+static char doc[] =
+ "Add new user/group ids to the authentication of selected processes";
+
+extern error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids);
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+ error_t err;
+ auth_t auth;
+ char *ids_rep = 0;
+ process_t proc_server = getproc();
+ struct frobauth frobauth = FROBAUTH_INIT;
+ struct idvec have_uids = IDVEC_INIT, have_gids = IDVEC_INIT;
+ struct argp argp = { 0, 0, 0, doc, child_argps };
+
+ frobauth.require_ids = 1;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ /* See what the invoking user is authorized to do. */
+ err = get_nonsugid_ids (&have_uids, &have_gids);
+ if (err)
+ error (52, err, "Cannot get invoking authentication");
+
+ /* Check passwords. */
+ err = ugids_verify_make_auth (&frobauth.ugids, &have_uids, &have_gids, 0, 0,
+ 0, 0, &auth);
+ if (err == EACCES)
+ error (15, 0, "Invalid password");
+ else if (err)
+ error (16, err, "Authentication failure");
+
+ if (frobauth.verbose)
+ /* A string showing which ids we will add. */
+ ids_rep = ugids_rep (&frobauth.ugids, 1, 1, 0, 0, 0);
+
+ /* Add the new authentication to each process. */
+ for (i = 0; i < frobauth.num_pids; i++)
+ {
+ mach_port_t msgport;
+ pid_t pid = frobauth.pids[i];
+ error_t err = proc_getmsgport (proc_server, pid, &msgport);
+
+ if (err)
+ error (0, err, "%d: Cannot get message port", pid);
+ else
+ {
+ if (! frobauth.dry_run)
+ err = msg_add_auth (msgport, auth);
+ if (err)
+ error (0, err, "%d: Cannot add authentication", pid);
+ else if (frobauth.verbose)
+ printf ("%d: Added %s\n", pid, ids_rep);
+ mach_port_deallocate (mach_task_self (), msgport);
+ }
+ }
+
+ return 0;
+}
diff --git a/utils/devprobe.c b/utils/devprobe.c
new file mode 100644
index 00000000..f3ea01f3
--- /dev/null
+++ b/utils/devprobe.c
@@ -0,0 +1,107 @@
+/* Check the existence of mach devices
+
+ Copyright (C) 1996, 1997 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 <argp.h>
+
+#include <hurd.h>
+#include <mach.h>
+#include <device/device.h>
+
+static const struct argp_option options[] = {
+ {"silent", 's', 0, 0, "Don't print devices found"},
+ {"quiet", 0, 0, OPTION_ALIAS},
+ {"first", 'f', 0, 0, "Stop after the first device found"},
+ {0}
+};
+static const char *args_doc = "DEVNAME...";
+static const char *doc = "Test for the existance of mach device DEVNAME..."
+ "\vThe exit status is 0 if any devices were found.";
+
+int
+main (int argc, char **argv)
+{
+ /* Print devices found? (otherwise only exit status matters) */
+ int print = 1;
+ /* If printing, print all devices on the command line that are found.
+ Otherwise, just the first one found is printed. */
+ int all = 1;
+ int found_one = 0;
+ mach_port_t device_master = MACH_PORT_NULL;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ error_t err;
+ device_t device;
+
+ case 's': case 'q':
+ /* Don't print any output. Since our exit status only reflects
+ whether any one of the devices exists, there's no point in
+ probing past the first one. */
+ print = all = 0; break;
+
+ case 'f':
+ all = 0; break;
+
+ case ARGP_KEY_ARG:
+ if (device_master == MACH_PORT_NULL)
+ {
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ argp_failure (state, 3, err, "Can't get device master port");
+ }
+
+ err = device_open (device_master, D_READ, arg, &device);
+ if (err == 0)
+ /* Got it. */
+ {
+ /* Free the device port we got. */
+ mach_port_deallocate (mach_task_self (), device);
+
+ if (print)
+ puts (arg);
+
+ if (! all)
+ /* Only care about the first device found, so declare success
+ and... */
+ exit (0);
+
+ found_one = 1;
+ }
+ else if (err != ED_NO_SUCH_DEVICE)
+ /* Complain about unexpected errors. */
+ argp_failure (state, 0, err, "%s", arg);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ exit (found_one ? 0 : 1);
+}
diff --git a/utils/frobauth-mod.c b/utils/frobauth-mod.c
new file mode 100644
index 00000000..06ccdbfa
--- /dev/null
+++ b/utils/frobauth-mod.c
@@ -0,0 +1,160 @@
+/* Modify the current authentication selected processes
+
+ Copyright (C) 1997, 1999 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+#include <error.h>
+
+#include "frobauth.h"
+
+/* For every pid in FROBAUTH, call MODIFY to change its argument UGIDS from
+ the current authentication to what it should be; CHANGE is whatever ids
+ the user specified. AUTHS, of length NUM_AUTHS, should be a vector of
+ auth ports giving whatever additional authentication is needed (besides
+ the process's current authentication). If the user specifies the
+ --verbose flags, PRINT_INFO is called after successfully installing the
+ new authentication in each process, to print a message about what
+ happened. True is returned if no errors occur, although most errors do
+ not cause termination, and error messages are printed for them. */
+error_t
+frobauth_modify (struct frobauth *frobauth,
+ const auth_t *auths, size_t num_auths,
+ error_t (*modify) (struct ugids *ugids,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void (*print_info) (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void *hook)
+{
+ int i;
+ int ok = 1;
+ size_t num_all_auths = num_auths + 1;
+ auth_t all_auths[num_all_auths];
+ pid_t cur_pid = getpid ();
+ process_t proc_server = getproc ();
+
+ bcopy (auths, all_auths, num_auths * sizeof *auths);
+
+ /* Map MODIFY over all pids. */
+ for (i = 0; i < frobauth->num_pids; i++)
+ if (frobauth->pids[i] != cur_pid)
+ {
+ mach_port_t msgport;
+ pid_t pid = frobauth->pids[i];
+ error_t err = proc_getmsgport (proc_server, pid, &msgport);
+
+ if (err)
+ error (0, err, "%d: Cannot get message port", pid);
+ else
+ {
+ task_t task;
+
+ err = proc_pid2task (proc_server, pid, &task);
+ if (err)
+ error (0, err, "%d", pid);
+ else
+ {
+ auth_t old_auth;
+
+ err = msg_get_init_port (msgport, task, INIT_PORT_AUTH,
+ &old_auth);
+ if (err)
+ error (0, err, "%d: Cannot get auth port", pid);
+ else
+ {
+ struct ugids old = UGIDS_INIT;
+
+ err = ugids_merge_auth (&old, old_auth);
+
+ if (err)
+ error (0, err, "%d: Cannot get auth port ids", pid);
+ else
+ {
+ struct ugids new = UGIDS_INIT;
+
+ /* Assume all gids that can be implied by some uid are
+ only present for that reason. */
+ ugids_imply_all (&old);
+
+ err = ugids_set (&new, &old);
+
+ err = (*modify) (&new, &frobauth->ugids, pid, hook);
+ if (err)
+ error (99, err, "%d: Cannot modify ids", pid);
+ else if (! ugids_equal (&new, &old))
+ {
+ if (! frobauth->dry_run)
+ {
+ auth_t new_auth;
+
+ /* Add the PID's old authentication to that
+ supplied by the calller. */
+ all_auths[num_all_auths - 1] = old_auth;
+
+ err = ugids_make_auth (&new,
+ all_auths,
+ num_all_auths,
+ &new_auth);
+ if (err)
+ error (0, err,
+ "%d: Authentication failure", pid);
+ else
+ {
+ err =
+ msg_set_init_port (msgport, task,
+ INIT_PORT_AUTH,
+ new_auth,
+ MACH_MSG_TYPE_MOVE_SEND);
+ if (err)
+ error (0, err, "%d", pid);
+ }
+
+ }
+
+ if (frobauth->verbose && !err)
+ (*print_info) (&new, &old, &frobauth->ugids,
+ pid, hook);
+
+ }
+ else if (frobauth->verbose)
+ printf ("%d: Nothing changed\n", pid);
+
+ ugids_fini (&new);
+ }
+
+ ugids_fini (&old);
+ mach_port_deallocate (mach_task_self (), old_auth);
+ }
+ mach_port_deallocate (mach_task_self (), task);
+ }
+ mach_port_deallocate (mach_task_self (), msgport);
+ }
+
+ if (err)
+ ok = 0; /* Something didn't work. */
+ }
+
+ return ok;
+}
diff --git a/utils/frobauth.c b/utils/frobauth.c
new file mode 100644
index 00000000..ccb7c3bb
--- /dev/null
+++ b/utils/frobauth.c
@@ -0,0 +1,282 @@
+/* Common interface for auth frobbing utilities
+
+ Copyright (C) 1997 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. */
+
+/* This file is rather a mess of intertwined argps; it shoud be redone as a
+ single level once argp can handle dynamic option frobbing. XXX */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+#include <hurd/process.h>
+
+#include "frobauth.h"
+#include "ugids.h"
+#include "pids.h"
+
+static struct argp common_argp;
+static struct argp fr_ugids_argp;
+
+static const struct argp_option common_options[] =
+{
+ {"verbose", 'v', 0, 0, "Print informational messages"},
+ {"dry-run", 'n', 0, 0, "Don't do anything, just print what would be done"},
+ { 0 }
+};
+static struct argp_child common_child_argps[] =
+{
+ { &pids_argp, 0, "Process selection:" },
+ { 0 }
+};
+
+static const char common_args_doc[] = "USER...";
+static const char common_doc[] =
+ "\vBy default, all processes in the current login collection are selected";
+
+static struct argp_child ugids_child_argps[] =
+{
+ { &ugids_argp, 0, "User/group ids:" },
+ { 0 }
+};
+
+/* An argp on top of the base frobauth argp that provides switchable
+ effective/available ids (XXX this junk should be moved into a single argp
+ [indeed, into ugids_argp] once argp can deal with the init routine
+ frobbing the argp source). */
+static const struct argp_option ea_options[] =
+{
+ {"available", 'a', 0, 0, "USER... specifies available ids"},
+ {"effective", 'e', 0, 0, "USER... specifies effective ids"},
+ { 0 }
+};
+
+static struct argp_child ea_posix_child_argps[] =
+{
+ { &common_argp },
+ { &fr_ugids_argp },
+ { 0 }
+};
+
+static struct argp_child no_ugids_child_argps[] =
+{
+ { &common_argp },
+ { 0 }
+};
+
+/* This holds state information that's only active during parsing. */
+struct frobauth_argp_state
+{
+ struct frobauth *frobauth;
+ struct pids_argp_params pids_argp_params;
+ struct ugids_argp_params ugids_argp_params;
+};
+
+static error_t
+common_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->input;
+ struct frobauth *frobauth = fs->frobauth;
+
+ switch (key)
+ {
+ case 'v':
+ frobauth->verbose = 1; break;
+ case 'n':
+ frobauth->dry_run = 1; break;
+
+ case ARGP_KEY_END:
+ if (frobauth->num_pids == 0)
+ /* No pids specified! By default, do the current login collection. */
+ {
+ pid_t lid;
+ error_t err = proc_getloginid (getproc (), getpid (), &lid);
+
+ if (err)
+ argp_failure (state, 2, err,
+ "Couldn't get current login collection");
+
+ err = add_fn_pids (&frobauth->pids, &frobauth->num_pids,
+ lid, proc_getloginpids);
+ if (err)
+ argp_failure (state, 3, err,
+ "%d: Couldn't get login collection pids", lid);
+
+ return err;
+ }
+ break;
+
+ case ARGP_KEY_INIT:
+ bzero (fs, sizeof *fs);
+ fs->frobauth = frobauth;
+ fs->pids_argp_params.pids = &frobauth->pids;
+ fs->pids_argp_params.num_pids = &frobauth->num_pids;
+ state->child_inputs[0] = &fs->pids_argp_params;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ free (fs);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+ugids_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->input;
+ struct frobauth *frobauth = fs->frobauth;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ fs->ugids_argp_params.ugids = &frobauth->ugids;
+ fs->ugids_argp_params.parse_user_args = 1;
+ fs->ugids_argp_params.default_user = frobauth->default_user;
+ fs->ugids_argp_params.require_ids = frobauth->require_ids;
+ fs->pids_argp_params.pids = &frobauth->pids;
+ fs->pids_argp_params.num_pids = &frobauth->num_pids;
+
+ state->child_inputs[0] = &fs->ugids_argp_params;
+
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+ea_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->hook;
+
+ switch (key)
+ {
+ case 'a':
+ fs->ugids_argp_params.user_args_are_available = 1;
+ break;
+ case 'e':
+ fs->ugids_argp_params.user_args_are_effective = 1;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (!fs->ugids_argp_params.user_args_are_effective
+ && !fs->ugids_argp_params.user_args_are_available)
+ /* Default to effective. */
+ fs->ugids_argp_params.user_args_are_effective = 1;
+
+ /* Let someone else parse the arg. */
+ return ARGP_ERR_UNKNOWN;
+
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ fs = state->hook = malloc (sizeof (struct frobauth_argp_state));
+ if (! fs)
+ return ENOMEM;
+
+ fs->frobauth = state->input;
+ state->child_inputs[0] = fs; /* Pass our state to the common parser. */
+ state->child_inputs[1] = fs; /* Pass our state to the common parser. */
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+posix_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->hook;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ fs = state->hook = malloc (sizeof (struct frobauth_argp_state));
+ if (! fs)
+ return ENOMEM;
+
+ fs->frobauth = state->input;
+ state->child_inputs[0] = fs; /* Pass our state to the common parser. */
+ state->child_inputs[1] = fs; /* Pass our state to the common parser. */
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+no_ugids_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->hook;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ fs = state->hook = malloc (sizeof (struct frobauth_argp_state));
+ if (! fs)
+ return ENOMEM;
+
+ fs->frobauth = state->input;
+ state->child_inputs[0] = fs; /* Pass our state to the common parser. */
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp common_argp =
+{
+ common_options, common_parse_opt, 0, common_doc, common_child_argps
+};
+static struct argp fr_ugids_argp =
+{
+ 0, ugids_parse_opt, 0, 0, ugids_child_argps
+};
+
+/* Parse frobauth args/options, where user args are added as single ids to
+ either the effective or available ids. */
+struct argp frobauth_ea_argp =
+{
+ ea_options, ea_parse_opt, 0, 0, ea_posix_child_argps
+};
+
+/* Parse frobauth args/options, where user args are added as posix user. */
+struct argp frobauth_posix_argp =
+{
+ 0, posix_parse_opt, 0, 0, ea_posix_child_argps
+};
+
+/* Parse frobauth args/options, where user args are added as posix user. */
+struct argp frobauth_no_ugids_argp =
+{
+ 0, no_ugids_parse_opt, 0, 0, no_ugids_child_argps
+};
diff --git a/utils/frobauth.doc b/utils/frobauth.doc
new file mode 100644
index 00000000..e4d1358f
--- /dev/null
+++ b/utils/frobauth.doc
@@ -0,0 +1,83 @@
+ -- Hurd process authentication frobbing commands --
+
+addauth -- Adds additional authority to selected processes, without changing
+ their identity (unless they previously had none)
+rmauth -- Removes authority
+setauth -- Changes the identity and authority of selected processes
+su -- Changes the identity and authority of selected processes, saving enough
+ authority to later undo the change
+unsu -- Attempts to undo the results of a previous su command
+
+Examples:
+
+As these commands effective existing processes rather than creating
+subshells, the following are all typed to the same shell.
+
+Starting with the ids I get from logging in as miles (the `ids' command shows
+all the ids in the process it was invoked from):
+
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles agids=10,10
+
+Note that first euid/egids is the traditional unix effective uid/gid, and,
+for instance, determines what identity files are created with; the 1st and
+2nd auids/agids are the posix `real' and `saved' ids. Now I add root
+authority:
+
+ (utils) addauth root
+ Password:
+ (utils) ids -tn
+ euids=miles,root egids=10,wheel auids=miles,miles agids=10,10
+
+The main id is still miles, but an effective root id is also present, meaning
+that the process has root privileges. The traditional `id' command hasn't
+yet been changed to print extended hurd ids, so it only knows about the
+additional group:
+
+ (utils) id
+ uid=9427(miles) gid=10 groups=10,0(wheel)
+
+Removing root puts us back where we started:
+
+ (utils) rmauth root
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles agids=10,10
+
+Now if we use su instead, it actually changes our process's identity (but
+note that the old ids are still around as available ids -- this means they
+the only privilege they grant is to become effective ids):
+
+ (utils) su
+ Password:
+ (utils) ids -tn
+ euids=root egids=wheel auids=root,root,miles,miles agids=wheel,wheel,10,10
+ (utils) id
+ uid=0(root) gid=0(wheel) groups=0(wheel)
+
+We can undo the su with unsu:
+
+ (utils) unsu
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles agids=10,10
+
+Now lets su again, to a different user:
+
+ (utils) su thomas
+ Password:
+ (utils) ids -tn
+ euids=thomas egids=11 auids=thomas,thomas,miles,miles agids=11,11,10,10
+
+If we now use another su command, instead of su, we can swap our identity;
+we don't need a password to do this, since the old ids are still there as
+available ids.
+
+ (utils) su miles
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles,thomas,thomas agids=10,10,11,11
+
+Now if we give unsu, we'll become thomas for good (this same effect may be
+had in one step with the `su --no-save' or `setauth' commands):
+
+ (utils) unsu
+ (utils) ids -tn
+ euids=thomas egids=11 auids=thomas,thomas agids=11,11
diff --git a/utils/frobauth.h b/utils/frobauth.h
new file mode 100644
index 00000000..2b9c78f1
--- /dev/null
+++ b/utils/frobauth.h
@@ -0,0 +1,71 @@
+/* Common interface for auth frobbing utilities
+
+ Copyright (C) 1997 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 __FROBAUTH_H__
+#define __FROBAUTH_H__
+
+#include <sys/types.h>
+#include <ugids.h>
+#include <argp.h>
+
+/* Structure describing which processes to frob, and how to frob them. */
+struct frobauth
+{
+ struct ugids ugids;
+ pid_t *pids;
+ size_t num_pids;
+ int verbose, dry_run; /* User options */
+ uid_t default_user; /* If none specified; -1 means none. */
+ int require_ids; /* If true, require some ids be specified. */
+};
+
+#define FROBAUTH_INIT { UGIDS_INIT, 0, 0, 0, 0, -1 }
+
+/* For every pid in FROBAUTH, call MODIFY to change its argument UGIDS from
+ the current authentication to what it should be; CHANGE is whatever ids
+ the user specified. AUTHS, of length NUM_AUTHS, should be a vector of
+ auth ports giving whatever additional authentication is needed (besides
+ the process's current authentication). If the user specifies the
+ --verbose flags, PRINT_INFO is called after successfully installing the
+ new authentication in each process, to print a message about what
+ happened. True is returned if no errors occur, although most errors do
+ not cause termination, and error messages are printed for them. */
+error_t frobauth_modify (struct frobauth *frobauth,
+ const auth_t *auths, size_t num_auths,
+ error_t (*modify) (struct ugids *ugids,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void (*print_info) (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void *hook);
+
+/* Parse frobauth args/options, where user args are added as single ids to
+ either the effective or available ids. */
+extern struct argp frobauth_ea_argp;
+
+/* Parse frobauth args/options, where user args are added as posix user. */
+extern struct argp frobauth_posix_argp;
+
+/* Parse frobauth args/options, with no user specifications. */
+extern struct argp frobauth_no_ugids_argp;
+
+#endif /* __FROBAUTH_H__ */
diff --git a/utils/fsysopts.c b/utils/fsysopts.c
new file mode 100644
index 00000000..79e40979
--- /dev/null
+++ b/utils/fsysopts.c
@@ -0,0 +1,128 @@
+/* Set options in a running filesystem
+
+ 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 <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <error.h>
+#include <argz.h>
+#include <version.h>
+
+#include <hurd/fsys.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fsysopts);
+
+static struct argp_option options[] =
+{
+ {"dereference", 'L', 0, 0, "If FILESYS is a symbolic link, follow it"},
+ {"recursive", 'R', 0, 0, "Pass these options to any child translators"},
+ {0, 0, 0, 0}
+};
+static char *args_doc = "FILESYS [FS_OPTION...]";
+static char *doc = "Get or set command line options for running translator FILESYS."
+"\vThe legal values for FS_OPTION depends on FILESYS, but\
+ some common ones are: --readonly, --writable, --remount, --sync[=INTERVAL],\
+ and --nosync.\n\nIf no options are supplied, FILESYS's existing options\
+ are printed";
+
+/* ---------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+
+ /* The file we use as a handle to get FSYS. */
+ char *node_name = 0;
+ file_t node;
+
+ /* The filesystem options vector, in '\0' separated format. */
+ char *argz = 0;
+ int argz_len = 0;
+
+ int deref = 0, recursive = 0;
+
+ /* Parse a command line option. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ node_name = arg;
+ err = argz_create (state->argv + state->next, &argz, &argz_len);
+ if (err)
+ error(3, err, "Can't create options vector");
+ state->next = state->argc; /* stop parsing */
+ break;
+
+ case 'R': recursive = 1; break;
+ case 'L': deref = 1; break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ node = file_name_lookup (node_name, (deref ? 0 : O_NOLINK), 0666);
+ if (node == MACH_PORT_NULL)
+ error (1, errno, "%s", node_name);
+
+ if (argz_len)
+ {
+ /* The filesystem we're passing options to. */
+ fsys_t fsys;
+
+ /* Get the filesystem for NODE. */
+ err = file_getcontrol (node, &fsys);
+ if (err)
+ error (2, err, "%s", node_name);
+
+ err = fsys_set_options (fsys, argz, argz_len, recursive);
+ if (err)
+ {
+ argz_stringify (argz, argz_len, ' ');
+ error(5, err, "%s: %s", node_name, argz);
+ }
+ }
+ else
+ {
+ err = file_get_fs_options (node, &argz, &argz_len);
+ if (err)
+ error (5, err, "%s", node_name);
+ argz_stringify (argz, argz_len, ' ');
+ puts (argz);
+ }
+
+ return 0;
+}
diff --git a/utils/ftpcp.c b/utils/ftpcp.c
new file mode 100644
index 00000000..4c4c151a
--- /dev/null
+++ b/utils/ftpcp.c
@@ -0,0 +1,397 @@
+/* Copy a file using the ftp protocol
+
+ Copyright (C) 1997 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <error.h>
+#include <argp.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#include <version.h>
+
+#include <ftpconn.h>
+
+#define COPY_SZ 65536
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ftpcp);
+
+#define OPT_SRC_U -3
+#define OPT_SRC_A -4
+#define OPT_SRC_P -5
+#define OPT_DST_U -6
+#define OPT_DST_A -7
+#define OPT_DST_P -8
+
+
+static struct argp_option options[] =
+{
+ {"user", 'u', "USER",0, "User to login as on both ftp servers"},
+ {"password", 'p', "PWD", 0, "USER's password"},
+ {"account", 'a', "ACCT",0, "Account to login as"},
+ {"src-user", OPT_SRC_U, "USER",0, "User to login as on the src ftp server"},
+ {"src-password",OPT_SRC_P, "PWD", 0, "The src USER's password"},
+ {"src-account", OPT_SRC_A, "ACCT",0, "Account to login as on the source server"},
+ {"dst-user", OPT_DST_U, "USER",0, "User to login as on the dst ftp server"},
+ {"dst-password",OPT_DST_P, "PWD", 0, "The dst USER's password"},
+ {"dst-account", OPT_DST_A, "ACCT",0, "Account to login as on the source server"},
+ {"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"},
+ {0, 0}
+};
+static char *args_doc = "SRC [DST]";
+static char *doc = "Copy file SRC over ftp to DST."
+"\vBoth SRC and DST may have the form HOST:FILE, FILE, or -, where - is"
+" standard input for SRC or standard output for DST, and FILE is a local"
+" file. DST may be a directory, in which case the basename of SRC is"
+" appended to make the actual destination filename.";
+
+/* customization hooks. */
+static struct ftp_conn_hooks conn_hooks = { 0 };
+
+static void
+cntl_debug (struct ftp_conn *conn, int type, const char *txt)
+{
+ char *type_str;
+
+ switch (type)
+ {
+ case FTP_CONN_CNTL_DEBUG_CMD: type_str = ">"; break;
+ case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break;
+ default: type_str = "?"; break;
+ }
+
+ fprintf (stderr, "%s%s%s\n", (char *)conn->hook ?: "", type_str, txt);
+}
+
+/* Return an ftp connection for the host NAME using PARAMS. If an error
+ occurrs, a message is printed the program exits. If CNAME is non-zero,
+ the host's canonical name, in mallocated storage, is returned in it. */
+struct ftp_conn *
+get_host_conn (char *name, struct ftp_conn_params *params, char **cname)
+{
+ error_t err;
+ struct hostent *he;
+ struct ftp_conn *conn;
+
+ he = gethostbyname (name);
+ if (! he)
+ error (10, 0, "%s: %s", name, hstrerror (h_errno));
+
+ params->addr = malloc (he->h_length);
+ if (! params->addr)
+ error (11, ENOMEM, "%s", name);
+
+ bcopy (he->h_addr_list[0], params->addr, he->h_length);
+ params->addr_len = he->h_length;
+ params->addr_type = he->h_addrtype;
+
+ err = ftp_conn_create (params, &conn_hooks, &conn);
+ if (err)
+ error (12, err, "%s", he->h_name);
+
+ if (cname)
+ *cname = strdup (he->h_name);
+
+ return conn;
+}
+
+static void
+cp (int src, const char *src_name, int dst, const char *dst_name)
+{
+ ssize_t rd;
+ static void *copy_buf = 0;
+
+ if (! copy_buf)
+ {
+ copy_buf = valloc (COPY_SZ);
+ if (! copy_buf)
+ error (13, ENOMEM, "Cannot allocate copy buffer");
+ }
+
+ while ((rd = read (src, copy_buf, COPY_SZ)) > 0)
+ do
+ {
+ int wr = write (dst, copy_buf, rd);
+ if (wr < 0)
+ error (14, errno, "%s", dst_name);
+ rd -= wr;
+ }
+ while (rd > 0);
+
+ if (rd != 0)
+ error (15, errno, "%s", src_name);
+}
+
+struct epoint
+{
+ char *name; /* Name, of the form HOST:FILE, FILE, or -. */
+ char *file; /* If remote, the FILE portion, or 0. */
+ int fd; /* A file descriptor to use. */
+ struct ftp_conn *conn; /* An ftp connection to use. */
+ struct ftp_conn_params params;
+};
+
+static void
+econnect (struct epoint *e, struct ftp_conn_params *def_params, char *name)
+{
+ char *rmt;
+
+ if (! e->name)
+ e->name = "-";
+
+ rmt = strchr (e->name, ':');
+ if (rmt)
+ {
+ error_t err;
+
+ *rmt++ = 0;
+
+ if (! e->params.user)
+ e->params.user = def_params->user;
+ if (! e->params.pass)
+ e->params.pass = def_params->pass;
+ if (! e->params.acct)
+ e->params.acct = def_params->acct;
+
+ e->conn = get_host_conn (e->name, &e->params, &e->name);
+ e->name = realloc (e->name, strlen (e->name) + 1 + strlen (rmt) + 1);
+ if (! e->name)
+ error (22, ENOMEM, "Cannot allocate name storage");
+
+ e->conn->hook = name;
+
+ err = ftp_conn_set_type (e->conn, "I");
+ if (err)
+ error (23, err, "%s: Cannot set connection type to binary",
+ e->name);
+
+ strcat (e->name, ":");
+ strcat (e->name, rmt);
+
+ e->file = rmt;
+ }
+ else if (e->params.user || e->params.pass || e->params.acct)
+ error (20, 0,
+ "%s: Ftp login parameter specified for a local endpoint (%s,%s,%s)",
+ e->name, e->params.user, e->params.pass, e->params.acct);
+ else
+ e->file = strdup (e->name);
+}
+
+static error_t
+eopen_wr (struct epoint *e, int *fd)
+{
+ if (e->conn)
+ return ftp_conn_start_store (e->conn, e->file, fd);
+ else if (strcmp (e->name, "-") == 0)
+ *fd = 1;
+ else
+ {
+ *fd = open (e->name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (*fd < 0)
+ return errno;
+ }
+ return 0;
+}
+
+static error_t
+eopen_rd (struct epoint *e, int *fd)
+{
+ if (e->conn)
+ return ftp_conn_start_retrieve (e->conn, e->file, fd);
+ else if (strcmp (e->name, "-") == 0)
+ *fd = 0;
+ else
+ {
+ *fd = open (e->name, O_RDONLY, 0666);
+ if (*fd < 0)
+ return errno;
+ }
+ return 0;
+}
+
+static void
+efinish (struct epoint *e)
+{
+ if (e->conn)
+ {
+ error_t err = ftp_conn_finish_transfer (e->conn);
+ if (err)
+ error (31, err, "%s", e->name);
+ }
+}
+
+/* Give a name which refers to a directory file, and a name in that
+ directory, this should return in COMPOSITE the composite name refering to
+ that name in that directory, in malloced storage. */
+error_t
+eappend (struct epoint *e,
+ const char *dir, const char *name,
+ char **composite)
+{
+ if (e->conn)
+ return ftp_conn_append_name (e->conn, dir, name, composite);
+ else
+ {
+ char *rval = malloc (strlen (dir) + 1 + strlen (name) + 1);
+
+ if (! rval)
+ return ENOMEM;
+
+ if (dir[0] == '/' && dir[1] == '\0')
+ stpcpy (stpcpy (rval, dir), name);
+ else
+ stpcpy (stpcpy (stpcpy (rval, dir), "/"), name);
+
+ *composite = rval;
+
+ return 0;
+ }
+}
+
+/* If the name of a file COMPOSITE is a composite name (containing both a
+ filename and a directory name), this function will return the name
+ component only in BASE, in malloced storage, otherwise it simply returns a
+ newly malloced copy of COMPOSITE in BASE. */
+error_t
+ebasename (struct epoint *e, const char *composite, char **base)
+{
+ if (e->conn)
+ return ftp_conn_basename (e->conn, composite, base);
+ else
+ {
+ *base = strdup (basename (composite));
+ return 0;
+ }
+}
+
+static void
+append_basename (struct epoint *dst, struct epoint *src)
+{
+ char *bname;
+ error_t err = ebasename (src, src->file, &bname);
+
+ if (err)
+ error (33, err, "%s: Cannot find basename", src->name);
+
+ err = eappend (dst, dst->file, bname, &dst->file);
+ if (err)
+ error (34, err, "%s: Cannot append name component", dst->name);
+
+ err = eappend (dst, dst->name, bname, &dst->name);
+ if (err)
+ error (35, err, "%s: Cannot append name component", dst->name);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct epoint rd = { 0 }, wr = { 0 };
+ struct ftp_conn_params def_params = { 0 }; /* default params */
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ switch (state->arg_num)
+ {
+ case 0: rd.name = arg; break;
+ case 1: wr.name = arg; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ break;
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+
+ case 'u': def_params.user = arg; break;
+ case 'p': def_params.pass = arg; break;
+ case 'a': def_params.acct = arg; break;
+
+ case OPT_SRC_U: rd.params.user = arg; break;
+ case OPT_SRC_P: rd.params.pass = arg; break;
+ case OPT_SRC_A: rd.params.acct = arg; break;
+
+ case OPT_DST_U: wr.params.user = arg; break;
+ case OPT_DST_P: wr.params.pass = arg; break;
+ case OPT_DST_A: wr.params.acct = arg; break;
+
+ case 'D': conn_hooks.cntl_debug = cntl_debug; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ econnect (&rd, &def_params, "SRC");
+ econnect (&wr, &def_params, "DST");
+
+ if (rd.conn && wr.conn)
+ /* Both endpoints are remote; directly copy between ftp servers. */
+ {
+ err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file);
+ if (err == EISDIR)
+ /* The destination name is a directory; try again with the source
+ basename appended. */
+ {
+ append_basename (&wr, &rd);
+ err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file);
+ }
+ if (err)
+ error (30, err, "Remote copy");
+ }
+ else
+ /* One endpoint is local, so do the copying ourself. */
+ {
+ int rd_fd, wr_fd;
+
+ err = eopen_rd (&rd, &rd_fd);
+ if (err)
+ error (31, err, "%s", rd.name);
+
+ err = eopen_wr (&wr, &wr_fd);
+ if (err == EISDIR)
+ /* The destination name is a directory; try again with the source
+ basename appended. */
+ {
+ append_basename (&wr, &rd);
+ err = eopen_wr (&wr, &wr_fd);
+ }
+ if (err)
+ error (32, err, "%s", wr.name);
+
+ cp (rd_fd, rd.name, wr_fd, wr.name);
+
+ close (rd_fd);
+ close (wr_fd);
+
+ efinish (&rd);
+ efinish (&wr);
+ }
+
+ exit (0);
+}
diff --git a/utils/ftpdir.c b/utils/ftpdir.c
new file mode 100644
index 00000000..4423f421
--- /dev/null
+++ b/utils/ftpdir.c
@@ -0,0 +1,330 @@
+/* Get a directory listing using the ftp protocol
+
+ Copyright (C) 1997 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 <unistd.h>
+#include <string.h>
+#include <error.h>
+#include <argp.h>
+#include <time.h>
+#include <netdb.h>
+
+#include <version.h>
+
+#include <ftpconn.h>
+
+#define COPY_SZ 65536
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ftpdir);
+
+static struct argp_option options[] =
+{
+ {"user", 'u', "USER",0, "User to login as on ftp server"},
+ {"password", 'p', "PWD", 0, "USER's password"},
+ {"account", 'a', "ACCT",0, "Account to login as"},
+ {"separator",'S', "SEP", 0, "String to separate multiple listings"},
+ {"prefix", 'P', "PFX", 0, "String to proceed listings; the first and second"
+ " occurances of %s are replace by HOST and DIR"},
+ {"host", 'h', "HOST",0, "Use HOST as a default host"},
+ {"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"},
+ {"intepret", 'i', 0, 0, "Parse the directory output"},
+ {0, 0}
+};
+static char *args_doc = "[([HOST:]DIR | HOST:)...]";
+static char *doc = "Get a directory listing over ftp from HOST:DIR."
+"\vIf HOST is not supplied in an argument any default value set by --host is"
+" used; if DIR is not supplied, the default directory of HOST is used."
+"\nIf multiple DIRs are supplied on the command line, each listing is"
+" prefixed by a newline (or SEP) and a line containing HOST:DIR: (or PFX).";
+
+/* customization hooks. */
+static struct ftp_conn_hooks conn_hooks = { 0 };
+
+static void
+cntl_debug (struct ftp_conn *conn, int type, const char *txt)
+{
+ char *type_str;
+
+ switch (type)
+ {
+ case FTP_CONN_CNTL_DEBUG_CMD: type_str = "."; break;
+ case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break;
+ default: type_str = "?"; break;
+ }
+
+ fprintf (stderr, "%s%s\n", type_str, txt);
+}
+
+struct ftpdir_host
+{
+ char *name;
+ struct ftp_conn_params params;
+ struct ftp_conn *conn;
+ struct ftpdir_host *next;
+};
+
+/* Return an ftp connection for the host NAME using PARAMS, and add an entry
+ for it to *HOSTS. If a connection already exists in HOSTS, it is returned
+ instead of making a new one. If an error occurrs, a message is printed and
+ 0 is returned. */
+static struct ftpdir_host *
+get_host_conn (char *name, struct ftp_conn_params *params,
+ struct ftpdir_host **hosts)
+{
+ error_t err;
+ struct ftpdir_host *h;
+ struct hostent *he;
+
+ for (h = *hosts; h; h = h->next)
+ if (strcmp (h->name, name) == 0)
+ return h;
+
+ he = gethostbyname (name);
+ if (! he)
+ {
+ error (0, 0, "%s: %s", name, hstrerror (h_errno));
+ return 0;
+ }
+
+ for (h = *hosts; h; h = h->next)
+ if (he->h_addrtype == h->params.addr_type
+ && he->h_length == h->params.addr_len
+ && bcmp (he->h_addr_list[0], h->params.addr, he->h_length) == 0)
+ return h;
+
+ h = malloc (sizeof (struct ftpdir_host));
+ if (! h)
+ {
+ error (0, ENOMEM, "%s", name);
+ return 0;
+ }
+
+ h->params = *params;
+ h->params.addr = malloc (he->h_length);
+ h->name = strdup (he->h_name);
+
+ if (!h->name || !h->params.addr)
+ err = ENOMEM;
+ else
+ {
+ bcopy (he->h_addr_list[0], h->params.addr, he->h_length);
+ h->params.addr_len = he->h_length;
+ h->params.addr_type = he->h_addrtype;
+ err = ftp_conn_create (&h->params, &conn_hooks, &h->conn);
+ }
+
+ if (err)
+ {
+ error (0, err, "%s", he->h_name);
+ if (h->name)
+ free (h->name);
+ if (h->params.addr)
+ free (h->params.addr);
+ free (h);
+ return 0;
+ }
+
+ h->next = *hosts;
+ *hosts = h;
+
+ return h;
+}
+
+static int
+ftpdir (char *dir, struct ftpdir_host *host)
+{
+ int data;
+ int rd;
+ error_t err;
+ static void *copy_buf = 0;
+ struct ftp_conn *conn = host->conn;
+ char *host_name = host->name;
+
+ err = ftp_conn_start_dir (conn, dir, &data);
+ if (err)
+ {
+ error (0, err, "%s:%s", host_name, dir);
+ return err;
+ }
+
+ if (! copy_buf)
+ {
+ copy_buf = valloc (COPY_SZ);
+ if (! copy_buf)
+ error (12, ENOMEM, "Cannot allocate copy buffer");
+ }
+
+ while ((rd = read (data, copy_buf, COPY_SZ)) > 0)
+ do
+ {
+ int wr = write (1, copy_buf, rd);
+ if (wr < 0)
+ error (13, errno, "stdout");
+ rd -= wr;
+ }
+ while (rd > 0);
+ if (rd != 0)
+ {
+ error (0, errno, "%s:%s", host_name, dir);
+ return errno;
+ }
+
+ close (data);
+
+ err = ftp_conn_finish_transfer (conn);
+ if (err)
+ {
+ error (0, err, "%s:%s", host_name, dir);
+ return err;
+ }
+
+ return 0;
+}
+
+static error_t
+pdirent (const char *name, const struct stat *st, const char *symlink_target,
+ void *hook)
+{
+ char timebuf[20];
+ strftime (timebuf, sizeof timebuf, "%Y-%m-%d %H:%M", localtime (&st->st_mtime));
+ printf ("%6o %2d %5d %5d %6ld %s %s\n",
+ st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size,
+ timebuf, name);
+ if (symlink_target)
+ printf (" -> %s\n",
+ symlink_target);
+ return 0;
+}
+
+static error_t
+ftpdir2 (char *dir, struct ftpdir_host *host)
+{
+ error_t err = ftp_conn_get_stats (host->conn, dir, 1, pdirent, 0);
+ if (err == ENOTDIR)
+ err = ftp_conn_get_stats (host->conn, dir, 0, pdirent, 0);
+ if (err)
+ error (0, err, "%s:%s", host->name, dir);
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct ftpdir_host *hosts = 0;
+ char *default_host = 0;
+ int interpret = 0;
+ struct ftp_conn_params params = { 0 };
+ char *sep = "\n";
+ char *pfx = "%s:%s:\n";
+ int use_pfx = 0;
+ int errs = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ {
+ char *host, *dir;
+
+ if (state->next < state->argc)
+ use_pfx = 1; /* Multiple arguments. */
+
+ dir = index (arg, ':');
+ if (dir)
+ {
+ host = arg;
+ *dir++ = '\0';
+ if (*host == '\0')
+ /* An argument of `:' */
+ host = default_host;
+ }
+ else
+ {
+ host = default_host;
+ dir = arg;
+ }
+
+ if (host)
+ {
+ struct ftpdir_host *h = get_host_conn (host, &params, &hosts);
+ if (h)
+ {
+ if (state->arg_num > 0)
+ fputs (sep, stdout);
+ if (use_pfx)
+ printf (pfx, h->name, dir);
+ if ((use_pfx && *pfx) || (state->arg_num > 0 && *sep))
+ fflush (stdout);
+ if (interpret)
+ errs |= ftpdir2 (dir, h);
+ else
+ errs |= ftpdir (dir, h);
+ }
+ errs = 1;
+ }
+ else
+ {
+ error (0, 0, "%s: No default host", arg);
+ errs = 1;
+ }
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (default_host)
+ {
+ struct ftpdir_host *h =
+ get_host_conn (default_host, &params, &hosts);
+ if (h)
+ errs |= ftpdir (0, h);
+ }
+ else
+ {
+ error (0, 0, "No default host");
+ errs = 1;
+ }
+ break;
+
+ return EINVAL;
+
+ case 'u': params.user = arg; break;
+ case 'p': params.pass = arg; break;
+ case 'a': params.acct = arg; break;
+ case 'h': default_host = arg; break;
+ case 'D': conn_hooks.cntl_debug = cntl_debug; break;
+ case 'P': pfx = arg; use_pfx = 1; break;
+ case 'S': sep = arg; break;
+ case 'i': interpret = 1; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (errs)
+ exit (10);
+ else
+ exit (0);
+}
diff --git a/utils/ids.c b/utils/ids.c
new file mode 100644
index 00000000..f8ad22c5
--- /dev/null
+++ b/utils/ids.c
@@ -0,0 +1,196 @@
+/* Show all hurd ids
+
+ 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 <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <unistd.h>
+#include <error.h>
+#include <ugids.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ids);
+
+static struct argp_option options[] =
+{
+ {"terse", 't', 0, 0, "Use a shorter one-line output format"},
+ {"effective", 'e', 0, 0, "Show effective ids"},
+ {"available", 'a', 0, 0, "Show available ids"},
+ {"uids", 'u', 0, 0, "Show user ids"},
+ {"gids", 'g', 0, 0, "Show group ids"},
+ {"names", 'n', 0, 0, "Show names of uids/gids"},
+ {"values", 'v', 0, 0, "Show numeric uids/gids"},
+ {0}
+};
+static char *args_doc = "[PID]";
+static char *doc = "Show hurd uids/gids."
+"\vIf PID is suppplied, show ids in that process.";
+
+/* ---------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ task_t task;
+ mach_port_t msgport;
+ int pid = -1;
+ auth_t auth = getauth ();
+ process_t proc = getproc ();
+ struct ugids ugids = UGIDS_INIT;
+ int show_eff = 0, show_avail = 0, show_uids = 0, show_gids = 0, terse = 0;
+ int show_names = 0, show_values = 0;
+
+ /* Print the given id vectors, using NAME for the prompt. */
+ void print_ids (struct idvec *uids, struct idvec *gids, char *name)
+ {
+ if (show_uids)
+ {
+ if (name && show_gids)
+ printf ("%s uids: ", name);
+ else if (show_gids)
+ printf ("uids: ");
+ else if (name)
+ printf ("%s: ", name);
+ printf ("%s\n",
+ idvec_uids_rep (uids, show_values, show_names, " "));
+ }
+ if (show_gids)
+ {
+ if (name && show_uids)
+ printf ("%s gids: ", name);
+ else if (show_uids)
+ printf ("gids: ");
+ else if (name)
+ printf ("%s: ", name);
+ printf ("%s\n", idvec_gids_rep (gids, show_values, show_names, " "));
+ }
+ }
+
+ /* Parse a command line option. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'e': show_eff = 1; break;
+ case 'a': show_avail = 1; break;
+ case 'u': show_uids = 1; break;
+ case 'g': show_gids = 1; break;
+ case 'n': show_names = 1; break;
+ case 'v': show_values = 1; break;
+ case 't': terse = 1; break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ {
+ pid = atoi (arg);
+ break;
+ }
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (!show_eff && !show_avail)
+ show_eff = show_avail = 1;
+ if (!show_uids && !show_gids)
+ show_uids = show_gids = 1;
+ if (!show_names && !show_values)
+ show_names = show_values = 1;
+
+ if (pid < 0)
+ /* We get our parent's authentication instead of our own because this
+ program is usually installed setuid. This should work even if it's
+ not installed setuid, using the auth port as authentication to the
+ msg_get_init_port rpc. */
+ pid = getppid ();
+
+ /* Get a msgport for PID, to which we can send requests. */
+ err = proc_getmsgport (proc, pid, &msgport);
+ if (err)
+ error (5, err, "%d: Cannot get process msgport", pid);
+
+ /* Try to get the task port to use as authentication. */
+ err = proc_pid2task (proc, pid, &task);
+
+ /* Now fetch the auth port; if we couldn't get the task port to use for
+ authentication, we try the (old) auth port instead. */
+ if (err)
+ err = msg_get_init_port (msgport, auth, INIT_PORT_AUTH, &auth);
+ else
+ err = msg_get_init_port (msgport, task, INIT_PORT_AUTH, &auth);
+ if (err)
+ error (6, err, "%d: Cannot get process authentication", pid);
+
+ mach_port_deallocate (mach_task_self (), msgport);
+ mach_port_deallocate (mach_task_self (), task);
+
+ /* Get the ids that AUTH represents. */
+ err = ugids_merge_auth (&ugids, auth);
+ if (err)
+ error (10, err, "Cannot get authentication ids");
+
+ /* Print them. */
+ if (terse)
+ /* Short output format. */
+ {
+ /* Since we use ugids_rep to format the output, just clear any fields
+ we don't want to show. */
+ if (! show_eff)
+ {
+ idvec_clear (&ugids.eff_uids);
+ idvec_clear (&ugids.eff_gids);
+ }
+ if (! show_avail)
+ {
+ idvec_clear (&ugids.avail_uids);
+ idvec_clear (&ugids.avail_gids);
+ }
+ if (! show_uids)
+ {
+ idvec_clear (&ugids.eff_uids);
+ idvec_clear (&ugids.avail_uids);
+ }
+ if (! show_gids)
+ {
+ idvec_clear (&ugids.eff_gids);
+ idvec_clear (&ugids.avail_gids);
+ }
+ printf ("%s\n", ugids_rep (&ugids, show_values, show_names, 0, " ","="));
+ }
+ else
+ /* Long output format */
+ {
+ if (show_eff)
+ print_ids (&ugids.eff_uids, &ugids.eff_gids,
+ show_avail ? "effective" : 0);
+ if (show_avail)
+ print_ids (&ugids.avail_uids, &ugids.avail_gids,
+ show_eff ? "available" : 0);
+ }
+
+ return 0;
+}
diff --git a/utils/login.c b/utils/login.c
new file mode 100644
index 00000000..275cfe52
--- /dev/null
+++ b/utils/login.c
@@ -0,0 +1,898 @@
+/* Hurdish login
+
+ 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 <string.h>
+#include <unistd.h>
+#include <paths.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <time.h>
+#include <assert.h>
+#include <version.h>
+#include <sys/mman.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/fcntl.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <envz.h>
+#include <idvec.h>
+#include <error.h>
+#include <timefmt.h>
+#include <hurd/lookup.h>
+#include <ugids.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (login);
+
+extern error_t
+exec_reauth (auth_t auth, int secure, int must_reauth,
+ mach_port_t *ports, unsigned num_ports,
+ mach_port_t *fds, unsigned num_fds);
+extern error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids);
+
+/* Defaults for various login parameters. */
+char *default_args[] = {
+ "SHELL=/bin/bash",
+ /* A ':' separated list of what to try if can't exec user's shell. */
+ "BACKUP_SHELLS=/bin/bash:" _PATH_BSHELL,
+ "HOME=/etc/login", /* Initial WD. */
+ "USER=login",
+ "UMASK=0",
+ "NAME=Not logged in",
+ "HUSHLOGIN=.hushlogin", /* Looked up relative new home dir. */
+ "MOTD=/etc/motd",
+ "PATH=/bin",
+ "NOBODY=login",
+ "NOAUTH_TIMEOUT=300", /* seconds before unauthed sessions die. */
+ 0
+};
+/* Default values for the new environment. */
+char *default_env[] = {
+ "PATH=/bin",
+ 0
+};
+
+/* Which things are copied from the login parameters into the environment. */
+char *copied_args[] = {
+ "USER", "SHELL", "HOME", "NAME", "VIA", "VIA_ADDR", "PATH", 0
+};
+
+static struct argp_option options[] =
+{
+ {"arg0", '0', "ARG", 0, "Make ARG the shell's argv[0]"},
+ {"envvar", 'e', "ENTRY", 0, "Add ENTRY to the environment"},
+ {"envvar-default", 'E', "ENTRY", 0, "Use ENTRY as a default environment variable"},
+ {"no-args", 'x', 0, 0, "Don't put login args into the environment"},
+ {"arg", 'a', "ARG", 0, "Add login parameter ARG"},
+ {"arg-default", 'A', "ARG", 0, "Use ARG as a default login parameter"},
+ {"no-environment-args", 'X', 0, 0, "Don't add the parent environment as default login params"},
+ {"no-login", 'L', 0, 0, "Don't modify the shells argv[0] to look"
+ " like a login shell"},
+ {"preserve-environment", 'p', 0, 0, "Inherit the parent's environment"},
+ {"via", 'h', "HOST", 0, "This login is from HOST"},
+ {"no-passwd", 'f', 0, 0, "Don't ask for passwords"},
+ {"paranoid", 'P', 0, 0, "Don't admit that a user doesn't exist"},
+ {"save", 's', 0, 0, "Keep the old available ids, and save the old"
+ " effective ids as available ids"},
+ {"shell-from-args", 'S', 0, 0, "Use the first shell arg as the shell to invoke"},
+ {"retry", 'R', "ARG", OPTION_ARG_OPTIONAL,
+ "Re-exec login with no users after non-fatal errors; if ARG is supplied,"
+ "add it to the list of args passed to login when retrying"},
+ {0, 0}
+};
+static struct argp_child child_argps[] =
+{
+ { &ugids_argp, 0, "Adding individual user/group ids:" },
+ { 0 }
+};
+static char *args_doc = "[USER [ARG...]]";
+static char *doc =
+"Exec a program with uids and/or the environment changed appropriately.\v"
+"To give args to the shell without specifying a user, use - for USER.\n"
+"Current login parameters include HOME, SHELL, USER, NAME, and ROOT.";
+
+/* Outputs whatever can be read from the io_t NODE to standard output, and
+ then close it. If NODE is MACH_PORT_NULL, assumes an error happened, and
+ prints an error message using ERRNO and STR. */
+static void
+cat (mach_port_t node, char *str)
+{
+ error_t err;
+ if (node == MACH_PORT_NULL)
+ err = errno;
+ else
+ for (;;)
+ {
+ char buf[1024], *data = buf;
+ mach_msg_type_number_t data_len = sizeof (buf);
+
+ err = io_read (node, &data, &data_len, -1, 16384);
+ if (err || data_len == 0)
+ break;
+ else
+ {
+ write (0, data, data_len);
+ if (data != buf)
+ munmap (data, data_len);
+ }
+ }
+ if (err)
+ error (0, errno, "%s", str);
+}
+
+/* Add a utmp entry based on the parameters in ARGS & ARGS_LEN. If
+ INHERIT_HOST is true, the host parameters in ARGS aren't to be trusted, so
+ try to get the host from the existing utmp entry (this only works if
+ re-logging in during an existing session). */
+static void
+add_utmp_entry (char *args, unsigned args_len, int inherit_host)
+{
+ struct utmp utmp;
+ char const *host = 0;
+ long addr = 0;
+
+ bzero (&utmp, sizeof (utmp));
+
+ gettimeofday (&utmp.ut_tv, 0);
+ strncpy (utmp.ut_name, envz_get (args, args_len, "USER") ?: "",
+ sizeof (utmp.ut_name));
+
+ if (! inherit_host)
+ {
+ char *via_addr = envz_get (args, args_len, "VIA_ADDR");
+ host = envz_get (args, args_len, "VIA");
+ if (host && strlen (host) > sizeof (utmp.ut_host))
+ host = via_addr ?: host;
+ if (via_addr)
+ addr = inet_addr (via_addr);
+ }
+
+ if (!host || !addr)
+ /* Get the host from the `existing utmp entry'. This is a crock. */
+ {
+ int tty_fd = 0;
+ char *tty = 0;
+
+ /* Search for a file descriptor naming a tty. */
+ while (!tty && tty_fd < 3)
+ tty = ttyname (tty_fd++);
+ if (tty)
+ /* Find the old utmp entry for TTY, and grab its host parameters. */
+ {
+ struct utmp *old_utmp;
+ strncpy (utmp.ut_line, basename (tty), sizeof (utmp.ut_line));
+ setutent ();
+ old_utmp = getutline (&utmp);
+ endutent ();
+ if (old_utmp)
+ {
+ if (! host)
+ host = old_utmp->ut_host;
+ if (! addr)
+ addr = old_utmp->ut_addr;
+ }
+ }
+ }
+
+ strncpy (utmp.ut_host, host ?: "", sizeof (utmp.ut_host));
+ utmp.ut_addr = addr;
+
+ login (&utmp);
+}
+
+/* Lookup the host HOST, and add entries for VIA (the host name), and
+ VIA_ADDR (the dotted decimal address) to ARGS & ARGS_LEN. */
+static error_t
+add_canonical_host (char **args, unsigned *args_len, char *host)
+{
+ struct hostent *he = gethostbyname (host);
+
+ if (he)
+ {
+ char *addr = 0;
+
+ /* Try and get an ascii version of the numeric host address. */
+ switch (he->h_addrtype)
+ {
+ case AF_INET:
+ addr = strdup (inet_ntoa (*(struct in_addr *)he->h_addr));
+ break;
+ }
+
+ if (addr && strcmp (he->h_name, addr) == 0)
+ /* gethostbyname() cheated! Lookup the host name via the address
+ this time to get the actual host name. */
+ he = gethostbyaddr (he->h_addr, he->h_length, he->h_addrtype);
+
+ if (he)
+ host = he->h_name;
+
+ if (addr)
+ {
+ envz_add (args, args_len, "VIA_ADDR", addr);
+ free (addr);
+ }
+ }
+
+ return envz_add (args, args_len, "VIA", host);
+}
+
+/* Add the `=' separated environment entry ENTRY to ENV & ENV_LEN, exiting
+ with an error message if we can't. */
+static void
+add_entry (char **env, unsigned *env_len, char *entry)
+{
+ char *name = strsep (&entry, "=");
+ error_t err = envz_add (env, env_len, name, entry);
+ if (err)
+ error (8, err, "Adding %s", entry);
+}
+
+/* Return in OWNED whether PID has an owner, or an error. */
+static error_t
+check_owned (process_t proc_server, pid_t pid, int *owned)
+{
+ int flags = PI_FETCH_TASKINFO;
+ char *waits = 0;
+ mach_msg_type_number_t num_waits = 0;
+ struct procinfo _pi, *pi = &_pi;
+ mach_msg_type_number_t pi_size = sizeof pi;
+ error_t err =
+ proc_getprocinfo (proc_server, pid, &flags, (procinfo_t *)&pi, &pi_size,
+ &waits, &num_waits);
+
+ if (! err)
+ {
+ *owned = !(pi->state & PI_NOTOWNED);
+ if (pi != &_pi)
+ munmap (pi, pi_size);
+ }
+
+ return err;
+}
+
+/* Kills the login session PID with signal SIG. */
+static void
+kill_login (process_t proc_server, pid_t pid, int sig)
+{
+ error_t err;
+ size_t num_pids;
+ pid_t self = getpid ();
+
+ do
+ {
+ pid_t _pids[num_pids = 20], *pids = _pids;
+ err = proc_getloginpids (proc_server, pid, &pids, &num_pids);
+ if (! err)
+ {
+ size_t i;
+ for (i = 0; i < num_pids; i++)
+ if (pids[i] != self)
+ kill (pids[i], sig);
+ if (pids != _pids)
+ munmap (pids, num_pids);
+ }
+ }
+ while (!err && num_pids > 0);
+}
+
+/* Looks at the login collection LID. If the root process (PID == LID) is
+ owned by someone, then exit (0), otherwise, if it's exited, exit (42). */
+static void
+check_login (process_t proc_server, int lid)
+{
+ int owned;
+ error_t err = check_owned (proc_server, lid, &owned);
+
+ if (err == ESRCH)
+ exit (42); /* Nothing left to watch. */
+ else
+ assert_perror (err);
+
+ if (owned)
+ exit (0); /* Our task is done. */
+}
+
+/* Forks a process which will kill the login session headed by PID after
+ TIMEOUT seconds if PID still has no owner. */
+static void
+dog (time_t timeout, pid_t pid, char **argv)
+{
+ if (fork () == 0)
+ {
+ char buf[25]; /* Be gratuitously pretty. */
+ char *name = basename (argv[0]);
+ time_t left = timeout;
+ struct timeval tv = { 0, 0 };
+ process_t proc_server = getproc ();
+
+ while (left)
+ {
+ time_t interval = left < 5 ? left : 5;
+
+ tv.tv_sec = left;
+
+ /* Frob ARGV so that ps show something nice. */
+ fmt_named_interval (&tv, 0, buf, sizeof buf);
+ asprintf (&argv[0], "(watchdog for %s %d: %s remaining)",
+ name, pid, buf);
+ argv[1] = 0;
+
+ sleep (interval);
+ left -= interval;
+
+ check_login (proc_server, pid);
+ }
+
+ check_login (proc_server, pid);
+
+ /* Give you-forgot-to-login message. */
+ tv.tv_sec = timeout;
+ fmt_named_interval (&tv, 0, buf, sizeof buf);
+
+ putc ('\n', stderr); /* Make sure our message starts a line. */
+ error (0, 0, "Timed out after %s.", buf);
+
+ /* Kill login session, trying to be nice about it. */
+ kill_login (proc_server, pid, SIGHUP);
+ sleep (5);
+ kill_login (proc_server, pid, SIGKILL);
+ exit (0);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ io_t node;
+ char *arg;
+ char *path;
+ error_t err = 0;
+ char *args = 0; /* The login parameters */
+ unsigned args_len = 0;
+ char *args_defs = 0; /* Defaults for login parameters. */
+ unsigned args_defs_len = 0;
+ char *env = 0; /* The new environment. */
+ unsigned env_len = 0;
+ char *env_defs = 0; /* Defaults for the environment. */
+ unsigned env_defs_len = 0;
+ char *parent_env = 0; /* The environment we got from our parent */
+ unsigned parent_env_len = 0;
+ int no_environ = 0; /* If false, use the env as default params. */
+ int no_args = 0; /* If false, put login params in the env. */
+ int inherit_environ = 0; /* True if we shouldn't clear our env. */
+ int no_passwd = 0; /* Don't bother verifying what we're doing. */
+ int no_login = 0; /* Don't prepend `-' to the shells argv[0]. */
+ int paranoid = 0; /* Admit no knowledge. */
+ int retry = 0; /* For some failures, exec a login shell. */
+ char *retry_args = 0; /* Args passed when retrying. */
+ unsigned retry_args_len = 0;
+ char *shell = 0; /* The shell program to run. */
+ char *sh_arg0 = 0; /* The shell's argv[0]. */
+ char *sh_args = 0; /* The args to the shell. */
+ unsigned sh_args_len = 0;
+ int shell_arg = 0; /* If there are shell args, use the first as
+ the shell name. */
+ struct ugids ugids = UGIDS_INIT; /* Authorization of the new shell. */
+ struct ugids_argp_params ugids_argp_params = { &ugids, 0, 0, 0, -1, 0 };
+ struct idvec parent_uids = IDVEC_INIT; /* Parent uids, -SETUID. */
+ struct idvec parent_gids = IDVEC_INIT; /* Parent gids, -SETGID. */
+ mach_port_t exec; /* The shell executable. */
+ mach_port_t cwd; /* The child's CWD. */
+ mach_port_t root; /* The child's root directory. */
+ mach_port_t ports[INIT_PORT_MAX]; /* Init ports for the new process. */
+ int ints[INIT_INT_MAX]; /* Init ints for it. */
+ mach_port_t fds[3]; /* File descriptors passed. */
+ mach_port_t auth; /* The new shell's authentication. */
+ mach_port_t proc_server = getproc ();
+ pid_t pid = getpid (), sid;
+
+ /* These three functions are to do child-authenticated lookups. See
+ <hurd/lookup.h> for an explanation. */
+ error_t use_child_init_port (int which, error_t (*operate)(mach_port_t))
+ {
+ return (*operate)(ports[which]);
+ }
+ mach_port_t get_child_fd_port (int fd)
+ {
+ return fd < 0 || fd > 2 ? __hurd_fail (EBADF) : fds[fd];
+ }
+ mach_port_t child_lookup (char *name, char *path, int flags)
+ {
+ mach_port_t port = MACH_PORT_NULL;
+ errno =
+ hurd_file_name_path_lookup (use_child_init_port, get_child_fd_port, 0,
+ name, path, flags, 0, &port, 0);
+ return port;
+ }
+
+ /* Print an error message with FMT, STR and ERR. Then, if RETRY is on,
+ exec a default login shell, otherwise exit with CODE (must be non-0). */
+ void fail (int code, error_t err, char *fmt, const char *str)
+ {
+ int retry_argc;
+ char **retry_argv;
+ char *via = envz_get (args, args_len, "VIA");
+ extern void _argp_unlock_xxx (); /* Secret unknown function. */
+
+ if (fmt)
+ error (retry ? 0 : code, err, fmt, str); /* May exit... */
+ else if (! retry)
+ exit (code);
+
+ if (via)
+ envz_add (&retry_args, &retry_args_len, "--via", via);
+ argz_insert (&retry_args, &retry_args_len, retry_args, argv[0]);
+
+ retry_argc = argz_count (retry_args, retry_args_len);
+ retry_argv = alloca ((retry_argc + 1) * sizeof (char *));
+ argz_extract (retry_args, retry_args_len, retry_argv);
+
+ /* Reinvoke ourselves with no userids or anything; shouldn't return. */
+ _argp_unlock_xxx (); /* Hack to get around problems with getopt. */
+ main (retry_argc, retry_argv);
+ exit (code); /* But if it does... */
+ }
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'p': inherit_environ = 1; break;
+ case 'x': no_args = 1; break;
+ case 'X': no_environ = 1; break;
+ case 'e': add_entry (&env, &env_len, arg); break;
+ case 'E': add_entry (&env_defs, &env_defs_len, arg); break;
+ case 'a': add_entry (&args, &args_len, arg); break;
+ case 'A': add_entry (&args_defs, &args_defs_len, arg); break;
+ case '0': sh_arg0 = arg; break;
+ case 'L': no_login = 1; break;
+ case 'f': no_passwd = 1; break;
+ case 'P': paranoid = 1; break;
+ case 'S': shell_arg = 1; break;
+
+ case 'R':
+ retry = 1;
+ if (arg)
+ {
+ err = argz_add (&retry_args, &retry_args_len, arg);
+ if (err)
+ error (10, err, "Adding retry arg %s", arg);
+ }
+ break;
+
+ case 'h':
+ add_canonical_host (&args, &args_len, arg);
+ retry = 1;
+ break;
+
+ case 's':
+ idvec_merge (&ugids.avail_uids, &parent_uids);
+ idvec_merge (&ugids.avail_gids, &parent_gids);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 0)
+ /* Program arguments. */
+ {
+ err = argz_create (state->argv + state->next - 1,
+ &sh_args, &sh_args_len);
+ state->next = state->argc; /* Consume all args */
+ if (err)
+ error (9, err, "Adding %s", arg);
+ break;
+ }
+
+ if (strcmp (arg, "-") == 0)
+ /* An explicit no-user-specified (so remaining args can be used
+ to set the program args). */
+ break;
+
+ if (isdigit (*arg))
+ err = ugids_set_posix_user (&ugids, atoi (arg));
+ else
+ {
+ struct passwd *pw = getpwnam (arg);
+ if (pw)
+ err = ugids_set_posix_user (&ugids, pw->pw_uid);
+ else if (paranoid)
+ /* Add a bogus uid so that password verification will
+ fail. */
+ idvec_add (&ugids.eff_uids, -1);
+ else
+ fail (10, 0, "%s: Unknown user", arg);
+ }
+
+ if (err)
+ fail (11, err, "%s: Can't set user!", arg);
+
+ break;
+
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = &ugids_argp_params;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = { options, parse_opt, args_doc, doc, child_argps };
+
+ /* Don't allow logins if the nologin file exists. */
+ node = file_name_lookup (_PATH_NOLOGIN, O_RDONLY, 0);
+ if (node != MACH_PORT_NULL)
+ {
+ cat (node, _PATH_NOLOGIN);
+ exit (40);
+ }
+
+ /* Put in certain last-ditch defaults. */
+ err = argz_create (default_args, &args_defs, &args_defs_len);
+ if (! err)
+ err = argz_create (default_env, &env_defs, &env_defs_len);
+ if (! err)
+ /* Set the default path using confstr() if possible. */
+ {
+ size_t path_len = confstr (_CS_PATH, 0, 0);
+ if (path_len > 0)
+ {
+ char path[path_len];
+ path_len = confstr (_CS_PATH, path, path_len);
+ if (path_len > 0)
+ err = envz_add (&env_defs, &env_defs_len, "PATH", path);
+ }
+ }
+ if (err)
+ error (23, err, "adding defaults");
+
+ err = argz_create (environ, &parent_env, &parent_env_len);
+
+ /* Get authentication of our parent, minus any setuid. */
+ get_nonsugid_ids (&parent_uids, &parent_gids);
+
+ /* Parse our options. */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ /* Check passwords where necessary. If no_passwd is set, then our parent
+ guarantees identity itself (where it is allowed), but otherwise
+ we want every UID fully checked. */
+ err = ugids_verify_make_auth (&ugids,
+ no_passwd ? &parent_uids : 0,
+ no_passwd ? &parent_gids : 0,
+ 0, 0, 0, 0, &auth);
+ if (err == EACCES)
+ fail (5, 0, "Invalid password", 0);
+ else if (err)
+ error (5, err, "Authentication failure");
+
+ /* Now that we've parsed the command line, put together all these
+ environments we've gotten from various places. There are two targets:
+ (1) the login parameters, and (2) the child environment.
+
+ The login parameters come from these sources (in priority order):
+ a) User specified (with the --arg option)
+ b) From the passwd file entry for the user being logged in as
+ c) From the parent environment, if --no-environ wasn't specified
+ d) From the user-specified defaults (--arg-default)
+ e) From last-ditch defaults given by the DEFAULT_* defines above
+
+ The child environment (constructed later) is from:
+ a) User specified (--environ)
+ b) From the login parameters (if --no-args wasn't specified)
+ c) From the parent environment, if --inherit-environ was specified
+ d) From the user-specified default env values (--environ-default)
+ e) From last-ditch defaults given by the DEFAULT_* defines above
+ */
+ {
+ struct passwd *pw;
+ char *passwd = 0; /* Login parameters from /etc/passwd */
+ unsigned passwd_len = 0;
+
+ /* Decide which password entry to get parameters from. */
+ if (ugids.eff_uids.num > 0)
+ pw = getpwuid (ugids.eff_uids.ids[0]); /* Effective uid */
+ else if (ugids.avail_uids.num > 0)
+ pw = getpwuid (ugids.avail_uids.ids[0]); /* Auxiliary uid */
+ else
+ /* No user! Try to used the `not-logged-in' user to set various
+ parameters. */
+ pw = getpwnam (envz_get (args, args_len, "NOBODY")
+ ?: envz_get (args_defs, args_defs_len, "NOBODY")
+ ?: "login");
+
+ if (pw)
+ {
+ envz_add (&passwd, &passwd_len, "HOME", pw->pw_dir);
+ envz_add (&passwd, &passwd_len, "SHELL", pw->pw_shell);
+ envz_add (&passwd, &passwd_len, "NAME", pw->pw_gecos);
+ envz_add (&passwd, &passwd_len, "USER", pw->pw_name);
+ }
+
+ /* Merge the login parameters. */
+ err = envz_merge (&args, &args_len, passwd, passwd_len, 0);
+ if (! err && ! no_environ)
+ err = envz_merge (&args, &args_len, parent_env, parent_env_len, 0);
+ if (! err)
+ err = envz_merge (&args, &args_len, args_defs, args_defs_len, 0);
+ if (err)
+ error (24, err, "merging parameters");
+
+ free (passwd);
+ }
+
+ err = proc_getsid (proc_server, pid, &sid);
+ assert_perror (err); /* This should never fail. */
+
+ if (!no_login
+ && (parent_uids.num != 0
+ || ugids.eff_uids.num + ugids.avail_uids.num > 0))
+ /* Make a new login collection (but only for real users). */
+ {
+ char *user = envz_get (args, args_len, "USER");
+ if (user && *user)
+ setlogin (user);
+ proc_make_login_coll (proc_server);
+
+ if (ugids.eff_uids.num + ugids.avail_uids.num == 0)
+ /* We're transiting from having some uids to having none, which means
+ this is probably a new login session. Unless specified otherwise,
+ set a timer to kill this session if it hasn't aquired any ids by
+ then. Note that we fork off the timer process before clearing the
+ process owner: because we're interested in killing unowned
+ processes, proc's in-same-login-session rule should apply to us
+ (allowing us to kill them), and this way they can't kill the
+ watchdog (because it *does* have an owner). */
+ {
+ char *to = envz_get (args, args_len, "NOAUTH_TIMEOUT");
+ time_t timeout = to ? atoi (to) : 0;
+ if (timeout)
+ dog (timeout, pid, argv);
+ }
+ }
+
+ if (ugids.eff_uids.num > 0)
+ proc_setowner (proc_server, ugids.eff_uids.ids[0], 0);
+ else
+ proc_setowner (proc_server, 0, 1); /* Clear the owner. */
+
+ /* Now start constructing the exec arguments. */
+ bzero (ints, sizeof (*ints) * INIT_INT_MAX);
+ arg = envz_get (args, args_len, "UMASK");
+ ints[INIT_UMASK] = arg && *arg ? strtoul (arg, 0, 8) : umask (0);
+
+ for (i = 0; i < 3; i++)
+ fds[i] = getdport (i);
+
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ ports[i] = MACH_PORT_NULL;
+ ports[INIT_PORT_PROC] = getproc ();
+ ports[INIT_PORT_CTTYID] = getcttyid ();
+ ports[INIT_PORT_CRDIR] = getcrdir (); /* May be replaced below. */
+ ports[INIT_PORT_CWDIR] = getcwdir (); /* " */
+
+ /* Now reauthenticate all of the ports we're passing to the child. */
+ err = exec_reauth (auth, 0, 1, ports, INIT_PORT_MAX, fds, 3);
+ if (err)
+ error (40, err, "Port reauth failure");
+
+ /* These are the default values for the child's root/cwd. We don't want to
+ modify PORTS just yet, because we use it to do child-authenticated
+ lookups. */
+ root = ports[INIT_PORT_CRDIR];
+ cwd = ports[INIT_PORT_CWDIR];
+
+ /* Find the shell executable (we copy the name, as ARGS may be changed). */
+ if (shell_arg && sh_args && *sh_args)
+ /* Special case for su mode: get the shell from the args if poss. */
+ {
+ shell = strdup (sh_args);
+ argz_delete (&sh_args, &sh_args_len, sh_args); /* Get rid of it. */
+ }
+ else
+ {
+ arg = envz_get (args, args_len, "SHELL");
+ if (arg && *arg)
+ shell = strdup (arg);
+ else
+ shell = 0;
+ }
+
+ path = envz_get (args, args_len, "PATH");
+ exec = shell ? child_lookup (shell, path, O_EXEC) : MACH_PORT_NULL;
+ if (exec == MACH_PORT_NULL)
+ {
+ char *backup = 0;
+ char *backups = envz_get (args, args_len, "BACKUP_SHELLS");
+ err = errno; /* Save original lookup errno. */
+
+ if (backups && *backups)
+ {
+ backups = strdupa (backups); /* Copy so we can trash it. */
+ while (exec == MACH_PORT_NULL && backups)
+ {
+ backup = strsep (&backups, ":, ");
+ if (*backup && (!shell || strcmp (shell, backup) != 0))
+ exec = child_lookup (backup, path, O_EXEC);
+ }
+ }
+
+ /* Give the error message, but only exit if we couldn't default. */
+ if (exec == MACH_PORT_NULL)
+ fail (1, err, "%s", shell);
+ else
+ error (0, err, "%s", shell);
+
+ /* If we get here, we looked up the default shell ok. */
+ shell = strdup (backup);
+ error (0, 0, "Using SHELL=%s", shell);
+ envz_add (&args, &args_len, "SHELL", shell);
+ err = 0; /* Don't emit random err msgs later! */
+ }
+
+ /* Now maybe change the cwd/root in the child. */
+
+ arg = envz_get (args, args_len, "HOME");
+ if (arg && *arg)
+ {
+ cwd = child_lookup (arg, 0, O_RDONLY);
+ if (cwd == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", arg);
+ error (0, 0, "Using HOME=/");
+ envz_add (&args, &args_len, "HOME", "/");
+ }
+ }
+
+ arg = envz_get (args, args_len, "ROOT");
+ if (arg && *arg)
+ {
+ root = child_lookup (arg, 0, O_RDONLY);
+ if (root == MACH_PORT_NULL)
+ fail (40, errno, "%s", arg);
+ }
+
+ /* Build the child environment. */
+ if (! no_args)
+ /* We can't just merge ARGS, because it may contain the parent
+ environment, which we don't always want in the child environment, so
+ we pick out only those values of args which actually *are* args. */
+ {
+ char **name;
+ char *user = envz_get (args, args_len, "USER");
+
+ for (name = copied_args; *name && !err; name++)
+ if (! envz_get (env, env_len, *name))
+ {
+ char *val = envz_get (args, args_len, *name);
+ if (val && *val)
+ err = envz_add (&env, &env_len, *name, val);
+ }
+
+ if (user)
+ /* Copy the user arg into the environment as LOGNAME. */
+ err = envz_add (&env, &env_len, "LOGNAME", user);
+ }
+ if (! err && inherit_environ)
+ err = envz_merge (&env, &env_len, parent_env, parent_env_len, 0);
+ if (! err)
+ err = envz_merge (&env, &env_len, env_defs, env_defs_len, 0);
+ if (err)
+ error (24, err, "Can't build environment");
+
+ if (! sh_arg0)
+ /* The shells argv[0] defaults to the basename of the shell. */
+ {
+ char *shell_base = rindex (shell, '/');
+ if (shell_base)
+ shell_base++;
+ else
+ shell_base = shell;
+
+ if (no_login)
+ sh_arg0 = shell_base;
+ else if (ugids.eff_uids.num + ugids.avail_uids.num == 0)
+ /* Use a special format for the argv[0] of a login prompt shell,
+ so that `ps' shows something informative in the COMMAND field.
+ This string must begin with a `-', the convention to tell the
+ shell to be a login shell (i.e. run .profile and the like). */
+ err = (asprintf (&sh_arg0, "-login prompt (%s)", shell_base) == -1
+ ? ENOMEM : 0);
+ else
+ /* Prepend a `-' to the name, which is the ancient canonical
+ way to tell the shell that it's a login shell. */
+ err = asprintf (&sh_arg0, "-%s", shell_base) == -1 ? ENOMEM : 0;
+ }
+ if (! err)
+ err = argz_insert (&sh_args, &sh_args_len, sh_args, sh_arg0);
+ if (err)
+ error (21, err, "Error building shell args");
+
+ /* Maybe output the message of the day. Note that we we the child's
+ authentication to do it, so that this program can't be used to read
+ arbitrary files! */
+ arg = envz_get (args, args_len, "MOTD");
+ if (arg && *arg)
+ {
+ char *hush = envz_get (args, args_len, "HUSHLOGIN");
+ mach_port_t hush_node =
+ (hush && *hush) ? child_lookup (hush, 0, O_RDONLY) : MACH_PORT_NULL;
+ if (hush_node == MACH_PORT_NULL)
+ {
+ mach_port_t motd_node = child_lookup (arg, 0, O_RDONLY);
+ if (motd_node != MACH_PORT_NULL)
+ cat (motd_node, arg);
+ }
+ else
+ mach_port_deallocate (mach_task_self (), hush_node);
+ }
+
+ /* Now that we don't need to use PORTS for lookups anymore, put the correct
+ ROOT and CWD in. */
+ ports[INIT_PORT_CRDIR] = root;
+ ports[INIT_PORT_CWDIR] = cwd;
+
+ /* Get rid of any accumulated null entries in env. */
+ envz_strip (&env, &env_len);
+
+ /* No more authentications to fail, so cross our fingers and add our utmp
+ entry. */
+
+ if (pid == sid)
+ /* Only add utmp entries for the session leader. */
+ add_utmp_entry (args, args_len, !idvec_contains (&parent_uids, 0));
+
+ if ((ugids.eff_uids.num | ugids.eff_gids.num) && !no_login)
+ {
+ char *tty = ttyname (0);
+ if (tty)
+ {
+ /* Change the terminal to be owned by the user. */
+ err = chown (tty,
+ ugids.eff_uids.num ? ugids.eff_uids.ids[0] : -1,
+ ugids.eff_gids.num ? ugids.eff_gids.ids[0] : -1);
+ if (err)
+ error (0, errno, "chown: %s", tty);
+ }
+ }
+
+ err = file_exec (exec, mach_task_self (), EXEC_DEFAULTS,
+ sh_args, sh_args_len, env, env_len,
+ fds, MACH_MSG_TYPE_COPY_SEND, 3,
+ ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ ints, INIT_INT_MAX,
+ 0, 0, 0, 0);
+ if (err)
+ error(5, err, "%s", shell);
+
+ return 0;
+}
diff --git a/utils/loginpr.sh b/utils/loginpr.sh
new file mode 100644
index 00000000..59924082
--- /dev/null
+++ b/utils/loginpr.sh
@@ -0,0 +1,20 @@
+#!/bin/bash -noprofile
+#
+# Traditional prompting login program
+#
+# This can be made the system default by making it the shell for the
+# pseudo-user `login'.
+#
+
+prompt='login: '
+user=''
+
+test "$_LOGIN_RETRY" && echo ''
+unset _LOGIN_RETRY
+
+until [ "$user" ]; do
+ echo -n "$prompt"
+ read user args || exit 0
+done
+
+exec login "$@" -p --paranoid -R-aSHELL="$0" -R-aMOTD -R-p -R-e_LOGIN_RETRY=yes "$user" $args
diff --git a/utils/mount.c b/utils/mount.c
new file mode 100644
index 00000000..75610bc6
--- /dev/null
+++ b/utils/mount.c
@@ -0,0 +1,575 @@
+/* Roughly Unix/Linux-compatible `mount' frontend for Hurd translators.
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ 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. */
+
+#include "../sutils/fstab.h"
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <hurd/fsys.h>
+#include <hurd/fshelp.h>
+#include <hurd/paths.h>
+
+#define SEARCH_FMTS _HURD "%sfs\0" _HURD "%s"
+#define DEFAULT_FSTYPE "ext2"
+
+static char *fstype = DEFAULT_FSTYPE;
+static char *device, *mountpoint;
+static int verbose;
+static char *options;
+static size_t options_len;
+static mach_msg_timeout_t timeout;
+
+static enum { mount, query } mode;
+static enum { qf_standard, qf_fstab, qf_translator } query_format;
+static struct fstab_argp_params fstab_params;
+
+
+static const struct argp_option argp_opts[] =
+{
+ {"timeout", 'T', "MILLISECONDS", 0, "Timeout for translator startup"},
+ {"format", 'p', "mount|fstab|translator", OPTION_ARG_OPTIONAL,
+ "Output format for query (no filesystem arguments)"},
+ {0, 0}
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct fstab_argp_params *params = state->input;
+ error_t err;
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = params; /* pass down to fstab_argp parser */
+ break;
+
+#define ARGZ(call) \
+ err = argz_##call; \
+ if (err) \
+ argp_failure (state, 100, ENOMEM, "%s", arg); \
+ break
+ case 'r': ARGZ (add (&options, &options_len, "ro"));
+ case 'w': ARGZ (add (&options, &options_len, "rw"));
+ case 'u': ARGZ (add (&options, &options_len, "remount"));
+ case 'o': ARGZ (add_sep (&options, &options_len, arg, ','));
+ case 'v': ++verbose; break;
+#undef ARGZ
+
+ case 'T':
+ {
+ char *end;
+ unsigned long int ms = strtoul (arg, &end, 10);
+ if (end && *end == '\0')
+ timeout = ms;
+ else
+ {
+ argp_error (state,
+ "--timeout needs a numeric argument (milliseconds)");
+ return EINVAL;
+ }
+ }
+ break;
+
+ case 'p':
+ if (arg == 0 || !strcasecmp (arg, "fstab"))
+ query_format = qf_fstab;
+ else if (!strcasecmp (arg, "mount"))
+ query_format = qf_standard;
+ else if (!strcasecmp (arg, "translator")
+ || !strcasecmp (arg, "showtrans"))
+ query_format = qf_translator;
+ else
+ {
+ argp_error (state, "invalid argument to --format");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_ARG:
+ if (mountpoint == 0) /* One arg: mountpoint */
+ mountpoint = arg;
+ else if (device == 0) /* Two args: device, mountpoint */
+ {
+ device = mountpoint;
+ mountpoint = arg;
+ }
+ else /* More than two args. */
+ {
+ argp_error (state, "too many arguments");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (! params->do_all)
+ {
+ params->do_all = 1;
+ mode = query;
+ }
+ break;
+
+ case ARGP_KEY_END:
+ if (params->do_all && mountpoint)
+ {
+ argp_error (state, "filesystem argument not allowed with --all");
+ return EINVAL;
+ }
+ if (mode == query && options_len != 0)
+ {
+ argp_error (state,
+ "mount options not allowed without filesystem argument");
+ return EINVAL;
+ }
+ if (mountpoint)
+ switch (argz_count (params->types, params->types_len))
+ {
+ default:
+ argp_error (state,
+ "multiple types not allowed with filesystem argument");
+ return EINVAL;
+ case 1:
+ fstype = params->types;
+ params->types = 0;
+ params->types_len = 0;
+ break;
+ case 0:
+ break;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const char doc[] = "Start active filesystem translators";
+static const char args_doc[] = "\
+DEVICE\t(in " _PATH_MNTTAB ")\n\
+DIRECTORY\t(in " _PATH_MNTTAB ")\n\
+[-t TYPE] DEVICE DIRECTORY\n\
+-a";
+static const struct argp_child argp_kids[] =
+{ { &fstab_argp, 0,
+ "Filesystem selection (if no explicit filesystem arguments given):", 2 },
+ { 0 } };
+static struct argp argp = { argp_opts, parse_opt, args_doc, doc, argp_kids };
+
+/* Mount one filesystem. */
+static error_t
+do_mount (struct fs *fs, int remount)
+{
+ error_t err;
+ char *fsopts, *o;
+ size_t fsopts_len;
+ char *mntopts;
+ size_t mntopts_len;
+ fsys_t mounted;
+
+ inline void explain (const char *command)
+ {
+ if (verbose)
+ {
+ const char *o;
+ printf ("%s %s", command, fs->mntent.mnt_dir);
+ for (o = fsopts; o; o = argz_next (fsopts, fsopts_len, o))
+ printf (" %s", o);
+ putchar ('\n');
+ }
+ }
+
+ err = fs_fsys (fs, &mounted);
+ if (err)
+ {
+ error (0, err, "cannot determine if %s is already mounted",
+ fs->mntent.mnt_fsname);
+ return err;
+ }
+
+
+ /* Produce an argz of translator option arguments from the
+ given FS's options and the command-line options. */
+
+#define ARGZ(call) \
+ err = argz_##call; \
+ if (err) \
+ error (3, ENOMEM, "collecting mount options"); \
+
+ if (fs->mntent.mnt_opts)
+ {
+ /* Append the fstab options to any specified on the command line. */
+ ARGZ (create_sep (fs->mntent.mnt_opts, ',', &mntopts, &mntopts_len));
+
+ /* Remove the `noauto' option, since it's for us not the filesystem. */
+ for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
+ if (!strcmp (o, MNTOPT_NOAUTO))
+ break;
+ if (o)
+ argz_delete (&mntopts, &mntopts_len, o);
+
+ ARGZ (append (&mntopts, &mntopts_len, options, options_len));
+ }
+ else
+ {
+ mntopts = options;
+ mntopts_len = options_len;
+ }
+
+ /* Convert the list of options into a list of switch arguments. */
+ fsopts = 0;
+ fsopts_len = 0;
+ for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
+ if (*o == '-') /* Allow letter opts `-o -r,-E', BSD style. */
+ {
+ ARGZ (add (&fsopts, &fsopts_len, o));
+ }
+ else
+ {
+ /* Prepend `--' to the option to make a long option switch,
+ e.g. `--ro' or `--rsize=1024'. */
+ char arg[2 + strlen (o) + 1];
+ arg[0] = arg[1] = '-';
+ memcpy (&arg[2], o, sizeof arg - 2);
+ ARGZ (add (&fsopts, &fsopts_len, arg));
+ }
+
+ if (mntopts != options)
+ free (mntopts);
+#undef ARGZ
+
+ if (remount)
+ {
+ if (mounted == MACH_PORT_NULL)
+ {
+ error (0, 0, "%s not already mounted", fs->mntent.mnt_fsname);
+ return EBUSY;
+ }
+
+ /* Send an RPC to request the new options, including --update. */
+ explain ("fsysopts");
+ err = fsys_set_options (mounted, fsopts, fsopts_len, 0);
+ if (err)
+ error (0, err, "cannot remount %s", fs->mntent.mnt_fsname);
+ return err;
+ }
+ else
+ {
+ /* Error during file lookup; we use this to avoid duplicating error
+ messages. */
+ error_t open_err = 0;
+ /* The control port for any active translator we start up. */
+ fsys_t active_control;
+ file_t node; /* Port to the underlying node. */
+ struct fstype *type;
+
+ /* The callback to start_translator opens NODE as a side effect. */
+ error_t open_node (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type)
+ {
+ node = file_name_lookup (fs->mntent.mnt_dir,
+ flags | O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ open_err = errno;
+ return open_err;
+ }
+
+ *underlying = node;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+ }
+
+ if (mounted != MACH_PORT_NULL)
+ {
+ error (0, 0, "%s already mounted", fs->mntent.mnt_fsname);
+ return EBUSY;
+ }
+
+ err = fs_type (fs, &type);
+ if (err)
+ {
+ error (0, err, "%s: cannot determine filesystem type",
+ fs->mntent.mnt_fsname);
+ return err;
+ }
+ if (type->program == 0)
+ {
+ error (0, 0, "%s: filesystem type `%s' unknown",
+ fs->mntent.mnt_fsname, type->name);
+ return EFTYPE;
+ }
+
+ /* Stick the translator program name in front of the option switches. */
+ err = argz_insert (&fsopts, &fsopts_len, fsopts, type->program);
+ /* Now stick the device name on the end as the last argument. */
+ if (!err)
+ err = argz_add (&fsopts, &fsopts_len, fs->mntent.mnt_fsname);
+ if (err)
+ error (3, ENOMEM, "collecting mount options");
+
+ /* Now we have a translator command line argz in FSOPTS. */
+
+ explain ("settrans -a");
+ err = fshelp_start_translator (open_node, fsopts,
+ fsopts, fsopts_len, timeout,
+ &active_control);
+ /* If ERR is due to a problem opening the translated node, we print
+ that name, otherwise, the name of the translator. */
+ if (open_err)
+ error (0, open_err, "cannot mount on %s", fs->mntent.mnt_dir);
+ else if (err)
+ error (0, err, "cannot start translator %s", fsopts);
+ else
+ {
+ err = file_set_translator (node, 0, FS_TRANS_SET|FS_TRANS_EXCL, 0,
+ 0, 0,
+ active_control, MACH_MSG_TYPE_COPY_SEND);
+ if (err == EBUSY)
+ error (0, 0, "%s already mounted on", fs->mntent.mnt_dir);
+ else if (err)
+ error (0, err, "cannot set translator on %s", fs->mntent.mnt_dir);
+ if (err)
+ fsys_goaway (active_control, FSYS_GOAWAY_FORCE);
+ mach_port_deallocate (mach_task_self (), active_control);
+ }
+
+ return err;
+ }
+}
+
+/* Report the state of one filesystem to stdout. */
+static error_t
+do_query (struct fs *fs)
+{
+ error_t err;
+ fsys_t fsys;
+ char _opts[200], *opts = _opts;
+ size_t opts_len = sizeof opts;
+ size_t nopts;
+
+ err = fs_fsys (fs, &fsys);
+ if (err)
+ return err;
+
+ if (fsys == MACH_PORT_NULL)
+ /* This filesystem is not mounted. Nothing to report. */
+ return 0;
+
+ err = fsys_get_options (fsys, &opts, &opts_len);
+ if (err)
+ {
+ error (0, err, "%s: cannot get filesystem options",
+ fs->mntent.mnt_fsname);
+ return err;
+ }
+
+ nopts = argz_count (opts, opts_len);
+ if (nopts == 0)
+ {
+ error (0, 0, "%s: fsys_get_options returned empty string",
+ fs->mntent.mnt_dir);
+ return EGRATUITOUS;
+ }
+
+ if (query_format == qf_translator)
+ {
+ argz_stringify (opts, opts_len, ' ');
+ printf ("%s: %s\n", fs->mntent.mnt_dir, opts);
+ }
+ else
+ {
+ char *argv[nopts];
+ const char *prog, *device;
+
+ inline const char *fsopt_string (const char *opt) /* canonicalize */
+ {
+ if (opt[0] == '-' && opt[1] == '-')
+ {
+ opt += 2;
+ if (!strcmp (opt, "readonly"))
+ return "ro";
+ if (!strcmp (opt, "writable"))
+ return "rw";
+ }
+ else
+ {
+ if (!strcmp (opt, "-r"))
+ return "ro";
+ if (!strcmp (opt, "-w"))
+ return "rw";
+ }
+ return opt;
+ }
+ inline void print_prog (const char *sfx)
+ {
+ if (!strncmp (_HURD, prog, sizeof _HURD - 1))
+ {
+ const char *type = &prog[sizeof _HURD - 1];
+ size_t len = strlen (type);
+ if (!strcmp (&type[len - 2], "fs"))
+ printf ("%.*s%s", (int) (len - 2), type, sfx);
+ else
+ printf ("%s%s", type, sfx);
+ }
+ else
+ printf ("%s%s", prog, sfx);
+ }
+ inline int print_opts (char sep)
+ {
+ char *opt = argz_next (opts, opts_len, prog);
+ if (opt == 0)
+ return 0;
+ fputs (fsopt_string (opt), stdout);
+ while ((opt = argz_next (opts, opts_len, opt)) != 0)
+ printf ("%c%s", sep, fsopt_string (opt));
+ return 1;
+ }
+
+ argz_extract (opts, opts_len, argv);
+ prog = argv[0];
+
+ if (nopts < 2)
+ device = fs->mntent.mnt_fsname;
+ else
+ {
+ static const char type_opt[] = "--store-type=";
+ device = argv[--nopts];
+ opts_len -= strlen (device) + 1;
+ if (!strncmp (type_opt, argv[nopts - 1], sizeof type_opt - 1))
+ {
+ asprintf ((char **) &device, "%s:%s",
+ &argv[nopts - 1][sizeof type_opt - 1], device);
+ opts_len -= strlen (argv[nopts - 1]) + 1;
+ }
+ }
+
+ switch (query_format)
+ {
+ case qf_standard:
+ printf ("%s on %s type ", device, fs->mntent.mnt_dir);
+ print_prog (" (");
+ if (print_opts (','))
+ puts (")");
+ else
+ puts ("defaults)");
+ break;
+
+ case qf_fstab:
+ printf ("%s\t%s\t", device, fs->mntent.mnt_dir);
+ print_prog ("\t");
+ printf ("%s\t%d %d\n",
+ print_opts (',') ? "" : "defaults",
+ fs->mntent.mnt_freq, fs->mntent.mnt_passno);
+ break;
+
+ case qf_translator: /* impossible */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ unsigned int remount;
+ struct fstab *fstab;
+ struct fs *fs;
+ error_t err;
+
+ argp_parse (&argp, argc, argv, 0, 0, &fstab_params);
+
+ if (!mountpoint && (fstab_params.types == 0
+
+ || !strncasecmp (fstab_params.types, "no", 2)))
+ {
+ /* Always add the type "swap" to the list of types to exclude. */
+ err = argz_add (&fstab_params.types, &fstab_params.types_len,
+ "no"MNTTYPE_SWAP);
+ if (err)
+ error (3, ENOMEM, "parsing arguments");
+ }
+
+ fstab = fstab_argp_create (&fstab_params, SEARCH_FMTS, sizeof SEARCH_FMTS);
+
+ if (device) /* two-argument form */
+ {
+ struct mntent m =
+ {
+ mnt_fsname: device,
+ mnt_dir: mountpoint,
+ mnt_type: fstype,
+ mnt_opts: 0,
+ mnt_freq: 0, mnt_passno: 0
+ };
+ struct fstype *fst;
+
+ err = fstypes_get (fstab->types, fstype, &fst);
+ if (err)
+ error (106, err, "cannot initialize type %s", fstype);
+ if (fst->program == 0)
+ error (2, 0, "filesystem type %s not recognized", fstype);
+
+ err = fstab_add_mntent (fstab, &m, &fs);
+ if (err)
+ error (2, err, "%s", mountpoint);
+ }
+ else if (mountpoint) /* one-argument form */
+ {
+ fs = fstab_find (fstab, mountpoint);
+ if (!fs)
+ error (2, 0, "%s: Unknown device or filesystem", mountpoint);
+ }
+ else
+ fs = 0;
+
+ /* This is a convenient way of checking for any `remount' options. */
+ remount = 0;
+ err = argz_replace (&options, &options_len, "remount", "update", &remount);
+ if (err)
+ error (3, ENOMEM, "collecting mount options");
+
+ if (fs != 0)
+ err = do_mount (fs, remount);
+ else
+ switch (mode)
+ {
+ case mount:
+ for (fs = fstab->entries; fs; fs = fs->next)
+ {
+ if (fstab_params.do_all && hasmntopt (&fs->mntent, MNTOPT_NOAUTO))
+ continue;
+ err |= do_mount (fs, remount);
+ }
+ break;
+
+ case query:
+ for (fs = fstab->entries; fs; fs = fs->next)
+ err |= do_query (fs);
+ break;
+ }
+
+ return err ? 1 : 0;
+}
diff --git a/utils/msgport.c b/utils/msgport.c
new file mode 100644
index 00000000..59362d87
--- /dev/null
+++ b/utils/msgport.c
@@ -0,0 +1,678 @@
+/* Send messages to selected processes
+
+ Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Jose M. Moya <josem@gnu.org>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+#include "pids.h"
+#include <sys/mman.h>
+
+/* From libc (not in hurd.h) */
+char *
+_hurd_canonicalize_directory_name_internal (file_t thisdir,
+ char *buf,
+ size_t size);
+
+const char *argp_program_version = STANDARD_HURD_VERSION (msgport);
+
+static const struct argp_option options[] =
+{
+ {0, 0}
+};
+
+static const char doc[] =
+"Send messages to selected processes";
+
+static const char args_doc[] =
+"";
+
+
+
+/* All command functions match this prototype. */
+typedef error_t (*cmd_func_t) (pid_t pid, mach_port_t msgport,
+ int argc, char *argv[]);
+
+/* One of these is created for each command given in the command line. */
+typedef struct cmd {
+ /* Function to execute for this command */
+ cmd_func_t f;
+
+ /* Array of arguments that will be passed to function F */
+ char **args;
+ size_t num_args;
+} cmd_t;
+
+
+/* Execute command CMD on process PID */
+error_t
+do_cmd (pid_t pid, cmd_t cmd)
+{
+ error_t err;
+ mach_port_t msgport;
+ process_t proc = getproc ();
+
+ /* Get a msgport for PID, to which we can send requests. */
+ err = proc_getmsgport (proc, pid, &msgport);
+ if (err)
+ error (1, err, "%d: Cannot get process msgport", pid);
+
+ err = (*cmd.f) (pid, msgport, cmd.num_args, cmd.args);
+ if (err)
+ error (2, err, "%d: Cannot execute command", pid);
+
+ mach_port_deallocate (mach_task_self (), msgport);
+ return 0;
+}
+
+
+/* All these functions, whose name start with cmd_, execute some
+ commands on the process PID, by sending messages (see msg.defs) to
+ its message port, which is MSGPORT. ARGC and ARGV are as in main.
+ They return zero iff successful. */
+
+/* Print the name and value of the environment variable ARGV[0].
+ Without arguments (ARGC==0), print the names and values of all
+ environment variables. */
+error_t
+cmd_getenv (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+
+ /* Memory will be vm_allocated by msg_get_* if the result does not
+ fit in buf. */
+ char buf[1024], *data = buf;
+ mach_msg_type_number_t len = sizeof (buf);
+
+ if (argc)
+ {
+ err = msg_get_env_variable (msgport, argv[0], &data, &len);
+ if (err)
+ return err;
+ printf ("%d: %s=%s\n", pid, argv[0], data);
+ }
+ else /* get the whole environment */
+ {
+ char *p;
+ err = msg_get_environment (msgport, &data, &len);
+ if (err)
+ return err;
+ for (p=data; p < data + len; p = strchr (p, '\0') + 1)
+ printf ("%d: %s\n", pid, p);
+ }
+ if (data != buf)
+ munmap (data, len);
+ return err;
+}
+
+/* Set environment variable ARGV[0] to the value ARGV[1]. */
+error_t
+cmd_setenv (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_set_env_variable (msgport, task, argv[0], argv[1], 1);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Clear environment. */
+error_t
+cmd_clearenv (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_set_environment (msgport, task, 0, 0);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Convert string STR in flags for file access modes. STR should be a
+ combination of `r', `w' and `x' (for read, write and execute modes
+ respectively). Other chars are ignored. */
+int
+str2flags (char *str)
+{
+ int flags = 0;
+ while (*str)
+ {
+ switch (*str)
+ {
+ case 'r': flags |= O_RDONLY; break;
+ case 'w': flags |= O_WRONLY; break;
+ case 'x': flags |= O_EXEC; break;
+ default:
+ /* ignore */
+ }
+ ++str;
+ }
+ return flags;
+}
+
+/* Set port associated to file descriptor FD of process PID, whose
+ message port is MSGPORT, to FILE. Used by
+ cmd_{setfd,stdin,stdout,stderr}. */
+error_t
+do_setfd (pid_t pid, mach_port_t msgport, size_t fd, file_t file)
+{
+ error_t err;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_set_fd (msgport, task, fd, file, MACH_MSG_TYPE_MOVE_SEND);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Set port associated to file descriptor ARGV[0] to the file ARGV[1].
+ File access mode is given by ARGV[2] (see str2flags). If no access
+ mode is given, the default is O_RDONLY. */
+error_t
+cmd_setfd (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = O_RDONLY;
+ file_t file;
+
+ if (argc > 2)
+ flags = str2flags(argv[2]);
+ file = file_name_lookup (argv[1], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, atoi (argv[0]), file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Set standard input to ARGV[0]. Optionally, ARGV[1] may specify the
+ file access mode (see str2flags). The default is O_RDONLY */
+error_t
+cmd_stdin (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = O_RDONLY;
+ file_t file;
+
+ if (argc > 1)
+ flags = str2flags(argv[1]);
+ file = file_name_lookup (argv[0], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, 0, file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Set standard output to ARGV[0]. Optionally, ARGV[1] may specify the
+ file access mode (see str2flags). The default is O_WRONLY */
+error_t
+cmd_stdout (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = O_WRONLY;
+ file_t file;
+
+ if (argc > 1)
+ flags = str2flags(argv[1]);
+ file = file_name_lookup (argv[0], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, 1, file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Set standard error to ARGV[0]. Optionally, ARGV[1] may specify the
+ file access mode (see str2flags). The default is O_RDONLY */
+error_t
+cmd_stderr (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = O_WRONLY;
+ file_t file;
+
+ if (argc > 1)
+ flags = str2flags(argv[1]);
+ file = file_name_lookup (argv[0], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, 2, file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Change current working directory to ARGV[0]. */
+error_t
+cmd_chcwdir (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ dir = file_name_lookup (argv[0], 0, 0);
+ if (dir == MACH_PORT_NULL)
+ return errno;
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), dir);
+ return err;
+ }
+ err = msg_set_init_port (msgport, task, INIT_PORT_CWDIR, dir,
+ MACH_MSG_TYPE_MOVE_SEND);
+ if (err)
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Change current working directory to current root directory. */
+error_t
+cmd_cdroot (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_get_init_port (msgport, task, INIT_PORT_CRDIR, &dir);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+ }
+ err = msg_set_init_port (msgport, task, INIT_PORT_CWDIR, dir,
+ MACH_MSG_TYPE_MOVE_SEND);
+ if (err)
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Change current root directory to ARGV[0]. */
+error_t
+cmd_chcrdir (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ dir = file_name_lookup (argv[0], 0, 0);
+ if (dir == MACH_PORT_NULL)
+ return errno;
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), dir);
+ return err;
+ }
+ err = msg_set_init_port (msgport, task, INIT_PORT_CRDIR, dir,
+ MACH_MSG_TYPE_MOVE_SEND);
+ if (err)
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Print current working directory. */
+error_t
+cmd_pwd (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_get_init_port (msgport, task, INIT_PORT_CWDIR, &dir);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+ }
+ printf ("%d: %s\n", pid,
+ _hurd_canonicalize_directory_name_internal(dir, NULL, 0));
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return 0;
+}
+
+/* Print current root directory */
+error_t
+cmd_getroot (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_get_init_port (msgport, task, INIT_PORT_CRDIR, &dir);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+ }
+ printf ("%d: %s\n", pid,
+ _hurd_canonicalize_directory_name_internal(dir, NULL, 0));
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return 0;
+}
+
+/* Change umask to ARGV[0] (octal value). Without arguments, print
+ the value of current umask. */
+error_t
+cmd_umask (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ mode_t umask;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ if (argc)
+ {
+ umask = strtol(argv[0], 0, 8);
+ err = msg_set_init_int (msgport, task, INIT_UMASK, umask);
+ }
+ else
+ {
+ err = msg_get_init_int (msgport, task, INIT_UMASK, &umask);
+ if (!err)
+ printf ("%d: %03o\n", pid, umask);
+ }
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+
+#define OA OPTION_ARG_OPTIONAL
+
+#define CMD_GETENV 1000
+#define CMD_SETENV 1001
+#define CMD_CLRENV 1002
+#define CMD_CHCWDIR 1003
+#define CMD_CHCRDIR 1004
+#define CMD_CDROOT 1005
+#define CMD_UMASK 1006
+#define CMD_SETFD 1007
+#define CMD_STDIN 1008
+#define CMD_STDOUT 1009
+#define CMD_STDERR 1010
+#define CMD_PWD 1011
+#define CMD_GETROOT 1012
+
+/* Params to be passed as the input when parsing CMDS_ARGP. */
+struct cmds_argp_params
+{
+ /* Array to be extended with parsed cmds. */
+ cmd_t **cmds;
+ size_t *num_cmds;
+};
+
+static const struct argp_option cmd_options[] =
+{
+ {"getenv", CMD_GETENV, "VAR", OA, "Get environment variable"},
+ {"printenv", 0, 0, OPTION_ALIAS},
+ {"setenv", CMD_SETENV, "VAR VALUE", 0, "Set environment variable"},
+ {"clearenv", CMD_CLRENV, 0, 0, "Clear environment"},
+ {"pwd", CMD_PWD, 0, 0, "Print current working directory"},
+ {"getcwd", 0, 0, OPTION_ALIAS},
+ {"getroot", CMD_GETROOT,0, 0, "Print current root directory"},
+ {"setfd", CMD_SETFD, "FD FILE [+rwxn]", 0, "Change file descriptor"},
+ {"stdin", CMD_STDIN, "FILE [+rwxn]", 0, "Change standard input"},
+ {"stdout", CMD_STDOUT, "FILE [+rwxn]", 0, "Change standard output"},
+ {"stderr", CMD_STDERR, "FILE [+rwxn]", 0, "Change standard error"},
+ {"chdir", CMD_CHCWDIR,"DIR", 0, "Change current working directory"},
+ {"cd", 0, 0, OPTION_ALIAS},
+ {"chroot", CMD_CHCRDIR,"DIR", 0, "Change current root directory"},
+ {"cdroot", CMD_CDROOT, 0, 0, "Change cwd to root directory"},
+ {"umask", CMD_UMASK, "MASK", OA, "Change umask"},
+ {0, 0}
+};
+
+/* Add a new command to the array of commands already parsed
+ reallocating it in malloced memory. FUNC is the command function.
+ MINARGS and MAXARGS are the minimum and maximum number of arguments
+ the parser will accept for this command. Further checking of the
+ arguments should be done in FUNC. ARG is the next argument in the
+ command line (probably the first argument for this command). STATE
+ is the argp parser state as used in parse_cmd_opt. */
+static error_t
+add_cmd (cmd_func_t func, size_t minargs, size_t maxargs,
+ char *arg, struct argp_state *state)
+{
+ cmd_t *cmd;
+ size_t i = 0;
+
+ struct cmds_argp_params *params = state->input;
+ size_t num_cmds = *params->num_cmds + 1;
+ cmd_t *cmds = realloc (*params->cmds, num_cmds * sizeof(cmd_t));
+
+ *params->cmds = cmds;
+ *params->num_cmds = num_cmds;
+
+ cmd = &cmds[num_cmds-1];
+ cmd->f = func;
+ cmd->args = 0;
+ if (maxargs)
+ {
+ cmd->args = malloc (maxargs * sizeof (char *));
+ if (arg)
+ cmd->args[i++] = arg;
+ while (i < maxargs
+ && state->argv[state->next]
+ && state->argv[state->next][0] != '-')
+ cmd->args[i++] = state->argv[state->next++];
+ }
+ if (i < minargs || i > maxargs)
+ argp_usage(state);
+ cmd->num_args = i;
+ return 0;
+}
+
+/* Parse one option/arg for the argp parser cmds_argp (see argp.h). */
+static error_t
+parse_cmd_opt (int key, char *arg, struct argp_state *state)
+{
+ /* A buffer used for rewriting command line arguments without dashes
+ for the parser to understand them. It gets realloced for each
+ successive arg that needs it, on the assumption that args don't
+ get parsed multiple times. */
+ static char *arg_hack_buf = 0;
+ switch (key)
+ {
+ case ARGP_KEY_ARG: /* Non-option argument. */
+ if (!isdigit (*arg) && !state->quoted)
+ {
+ /* Make state->next point to the just parsed argument to
+ re-parse it with 2 dashes prepended. */
+ size_t len = strlen (arg) + 1;
+ arg_hack_buf = realloc (arg_hack_buf, 2 + len);
+ state->argv[--state->next] = arg_hack_buf;
+ state->argv[state->next][0] = '-';
+ state->argv[state->next][1] = '-';
+ memcpy (&state->argv[state->next][2], arg, len);
+ break;
+ }
+ else
+ return ARGP_ERR_UNKNOWN;
+ case CMD_CHCWDIR:
+ add_cmd (&cmd_chcwdir, 0, 1, arg, state);
+ break;
+ case CMD_CHCRDIR:
+ add_cmd (&cmd_chcrdir, 1, 1, arg, state);
+ break;
+ case CMD_CDROOT:
+ add_cmd (&cmd_cdroot, 0, 0, arg, state);
+ break;
+ case CMD_PWD:
+ add_cmd (&cmd_pwd, 0, 0, arg, state);
+ break;
+ case CMD_GETROOT:
+ add_cmd (&cmd_getroot, 0, 0, arg, state);
+ break;
+ case CMD_UMASK:
+ add_cmd (&cmd_umask, 0, 1, arg, state);
+ break;
+ case CMD_GETENV:
+ add_cmd (&cmd_getenv, 0, 1, arg, state);
+ break;
+ case CMD_SETENV:
+ add_cmd (&cmd_setenv, 2, 2, arg, state);
+ break;
+ case CMD_CLRENV:
+ add_cmd (&cmd_clearenv, 0, 0, arg, state);
+ break;
+ case CMD_SETFD:
+ add_cmd (&cmd_setfd, 2, 3, arg, state);
+ break;
+ case CMD_STDIN:
+ add_cmd (&cmd_stdin, 1, 2, arg, state);
+ break;
+ case CMD_STDOUT:
+ add_cmd (&cmd_stdout, 1, 2, arg, state);
+ break;
+ case CMD_STDERR:
+ add_cmd (&cmd_stderr, 1, 2, arg, state);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Filtering of help output strings for cmds_argp parser. Return a
+ malloced replacement for TEXT as the arguments doc string. See
+ argp.h for details. */
+static char *
+help_filter (int key, const char *text, void *input)
+{
+ if (key == ARGP_KEY_HELP_ARGS_DOC)
+ return strdup ("CMD [ARG...]");
+
+ return (char *)text;
+}
+
+/* An argp parser for selecting a command (see argp.h). */
+struct argp cmds_argp = { cmd_options, parse_cmd_opt, 0, 0, 0, help_filter };
+
+
+
+int
+main(int argc, char *argv[])
+{
+ cmd_t *cmds = 0;
+ size_t num_cmds = 0;
+ struct cmds_argp_params cmds_argp_params = { &cmds, &num_cmds };
+ pid_t *pids = 0; /* User-specified pids. */
+ size_t num_pids = 0;
+ struct pids_argp_params pids_argp_params = { &pids, &num_pids, 0 };
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ state->child_inputs[0] = &cmds_argp_params;
+ state->child_inputs[1] = &pids_argp_params;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (!num_cmds || !num_pids)
+ argp_usage (state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp_child argp_kids[] =
+ { { &cmds_argp, 0,
+ "Commands:", 2},
+ { &pids_argp, 0,
+ "Process selection:", 3},
+ {0} };
+
+ struct argp argp = { options, parse_opt, args_doc, doc, argp_kids };
+
+ error_t err;
+ pid_t cur_pid = getpid ();
+ pid_t pid;
+ cmd_t cmd;
+ size_t i, j;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ for (i = 0; i < num_pids; ++i)
+ {
+ pid = pids[i];
+ if (pid != cur_pid)
+ for (j = 0; j < num_cmds; ++j)
+ {
+ cmd = cmds[j];
+ if ((err = do_cmd (pid, cmd)))
+ error (2, err, "%d: Cannot execute command", pid);
+ }
+ }
+
+ exit (0);
+}
diff --git a/utils/nonsugid.c b/utils/nonsugid.c
new file mode 100644
index 00000000..71cd3d71
--- /dev/null
+++ b/utils/nonsugid.c
@@ -0,0 +1,63 @@
+/* Get our ids, minus any setuid result
+
+ Copyright (C) 1995,96,97,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 <errno.h>
+#include <idvec.h>
+#include <hurd.h>
+
+/* Make sure that the [UG]IDS are filled in. To make them useful for
+ su'ing, each is the avail ids with the saved set-ID removed, and all
+ effective ids but the first appended; this gets rid of the effect of
+ being suid, and is useful as a new process's avail id list (e.g., the
+ real id is right). */
+error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids)
+{
+ if (uids->num == 0 && gids->num == 0)
+ {
+ error_t err = 0;
+ static auth_t auth = MACH_PORT_NULL;
+ struct idvec *p_eff_uids = make_idvec ();
+ struct idvec *p_eff_gids = make_idvec ();
+
+ if (!p_eff_uids || !p_eff_gids)
+ err = ENOMEM;
+
+ if (auth == MACH_PORT_NULL)
+ auth = getauth ();
+
+ if (! err)
+ err = idvec_merge_auth (p_eff_uids, uids, p_eff_gids, gids, auth);
+ if (! err)
+ {
+ idvec_delete (p_eff_uids, 0); /* Remove effective ID from setuid. */
+ idvec_delete (p_eff_gids, 0);
+ idvec_delete (uids, 1); /* Remove saved set-ID from setuid. */
+ idvec_delete (gids, 1);
+ if (! err)
+ err = idvec_merge (uids, p_eff_uids);
+ if (! err)
+ err = idvec_merge (gids, p_eff_gids);
+ }
+
+ return err;
+ }
+ else
+ return 0;
+}
diff --git a/utils/parse.c b/utils/parse.c
new file mode 100644
index 00000000..5334dbda
--- /dev/null
+++ b/utils/parse.c
@@ -0,0 +1,185 @@
+/* Random helpful option parsing functions
+
+ 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <error.h>
+
+#include "parse.h"
+
+/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is
+ empty and DEFAULT_ADD_FN isn't NULL, then call DEFAULT_ADD_FN instead. */
+error_t
+_parse_strlist (char *arg,
+ error_t (*add_fn)(const char *str, struct argp_state *state),
+ error_t (*default_add_fn)(struct argp_state *state),
+ const char *type_name, struct argp_state *state)
+{
+ if (arg)
+ while (isspace(*arg))
+ arg++;
+
+ if (arg == NULL || *arg == '\0')
+ if (default_add_fn)
+ return (*default_add_fn)(state);
+ else
+ {
+ argp_error (state, "Empty %s list", type_name);
+ return EINVAL;
+ }
+ else
+ {
+ error_t err = 0;
+ char *end = arg;
+
+ void mark_end()
+ {
+ *end++ = '\0';
+ while (isspace(*end))
+ end++;
+ }
+ error_t parse_element()
+ {
+ char *cur = arg;
+ if (*cur == '\0')
+ {
+ argp_error (state, "Empty element in %s list", type_name);
+ return EINVAL;
+ }
+ arg = end;
+ return (*add_fn)(cur, state);
+ }
+
+ while (*end != '\0' && !err)
+ switch (*end)
+ {
+ case ' ': case '\t':
+ mark_end();
+ if (*end == ',')
+ mark_end();
+ err = parse_element();
+ break;
+ case ',':
+ mark_end();
+ err = parse_element();
+ break;
+ default:
+ end++;
+ }
+
+ if (! err)
+ err = parse_element();
+
+ return err;
+ }
+}
+
+/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is
+ empty and DEFAULT_FN isn't NULL, then call ADD_FN on the resutl of calling
+ DEFAULT_FN instead, otherwise signal an error. */
+error_t
+parse_strlist (char *arg,
+ error_t (*add_fn)(const char *str, struct argp_state *state),
+ const char *(*default_fn)(struct argp_state *state),
+ const char *type_name, struct argp_state *state)
+{
+ error_t default_str_add (struct argp_state *state)
+ { return (*add_fn)((*default_fn)(state), state); }
+ return _parse_strlist (arg, add_fn, default_str_add, type_name, state);
+}
+
+/* For each numeric string in the comma-separated list in ARG, call ADD_FN;
+ if ARG is empty and DEFAULT_FN isn't NULL, then call DEF_FN to get a number,
+ and call ADD_FN on that, otherwise signal an error. If any member of the
+ list isn't a number, and LOOKUP_FN isn't NULL, then it is called to return
+ an integer for the string. LOOKUP_FN should signal an error itself it
+ there's some problem parsing the string. */
+error_t
+parse_numlist (char *arg,
+ error_t (*add_fn)(unsigned num, struct argp_state *state),
+ int (*default_fn)(struct argp_state *state),
+ int (*lookup_fn)(const char *str, struct argp_state *state),
+ const char *type_name, struct argp_state *state)
+{
+ error_t default_num_add() { return (*add_fn)((*default_fn)(state), state); }
+ error_t add_num_str(const char *str, struct argp_state *state)
+ {
+ const char *p;
+ for (p = str; *p != '\0'; p++)
+ if (!isdigit(*p))
+ {
+ if (lookup_fn)
+ return (*add_fn)((*lookup_fn)(str, state), state);
+ else
+ {
+ argp_error (state, "%s: Invalid %s", p, type_name);
+ return EINVAL;
+ }
+ return 0;
+ }
+ return (*add_fn) (atoi (str), state);
+ }
+ return _parse_strlist(arg, add_num_str, default_fn ? default_num_add : 0,
+ type_name, state);
+}
+
+/* Return the index of which of a set of strings ARG matches, including
+ non-ambiguous partial matches. CHOICE_FN should be a function that
+ returns the name of the Nth option, or 0 if N is out of range, and KIND
+ should be a string that describes what's being matched, for use in error
+ messages. If ALLOW_MISMATCHES is true, then -1 is returned in the event
+ that ARG matches no entry , otherwise, an error message is printed and the
+ program exits in this event. If ARG is an ambiguous match, an error
+ message is printed and the program exits. */
+int
+parse_enum (const char *arg,
+ const char *(*choice_fn)(unsigned n),
+ const char *kind, int allow_mismatches,
+ struct argp_state *state)
+{
+ const char *choice;
+ int arglen = strlen (arg);
+ int n = 0;
+ int partial_match = -1;
+
+ while ((choice = (*choice_fn)(n)) != NULL)
+ if (strcasecmp (choice, arg) == 0)
+ return n;
+ else
+ {
+ if (strncasecmp (choice, arg, arglen) == 0)
+ {
+ if (partial_match >= 0)
+ {
+ argp_error (state, "%s: Ambiguous %s", arg, kind);
+ return -1;
+ }
+ else
+ partial_match = n;
+ }
+ n++;
+ }
+
+ if (partial_match < 0 && !allow_mismatches)
+ argp_error (state, "%s: Invalid %s", arg, kind);
+
+ return partial_match;
+}
diff --git a/utils/parse.h b/utils/parse.h
new file mode 100644
index 00000000..9b6f31f9
--- /dev/null
+++ b/utils/parse.h
@@ -0,0 +1,63 @@
+/* Random helpful option parsing functions
+
+ Copyright (C) 1995, 1996, 1997 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 __PARSE_H__
+#define __PARSE_H__
+
+#include <errno.h>
+#include <argp.h>
+
+/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is
+ empty and DEFAULT_FN isn't NULL, then call ADD_FN on the resutl of calling
+ DEFAULT_FN instead, otherwise signal an error. */
+extern error_t
+parse_strlist (char *arg,
+ error_t (*add_fn)(const char *str, struct argp_state *state),
+ const char *(*default_fn)(struct argp_state *state),
+ const char *type_name, struct argp_state *state);
+
+/* For each numeric string in the comma-separated list in ARG, call ADD_FN;
+ if ARG is empty and DEFAULT_FN isn't NULL, then call DEF_FN to get a number,
+ and call ADD_FN on that, otherwise signal an error. If any member of the
+ list isn't a number, and LOOKUP_FN isn't NULL, then it is called to return
+ an integer for the string. LOOKUP_FN should signal an error itself it
+ there's some problem parsing the string. */
+extern error_t
+parse_numlist (char *arg,
+ error_t (*add_fn)(unsigned num, struct argp_state *state),
+ int (*default_fn)(struct argp_state *state),
+ int (*lookup_fn)(const char *str, struct argp_state *state),
+ const char *type_name, struct argp_state *state);
+
+/* Return the index of which of a set of strings ARG matches, including
+ non-ambiguous partial matches. CHOICE_FN should be a function that
+ returns the name of the Nth option, or 0 if N is out of range, and KIND
+ should be a string that describes what's being matched, for use in error
+ messages. If ALLOW_MISMATCHES is true, then -1 is returned in the event
+ that ARG matches no entry , otherwise, an error message is printed and the
+ program exits in this event. If ARG is an ambiguous match, an error
+ message is printed and the program exits. */
+extern int
+parse_enum (const char *arg,
+ const char *(*choice_fn)(unsigned n),
+ const char *kind, int allow_mismatches,
+ struct argp_state *state);
+
+#endif /* __PARSE_H__ */
diff --git a/utils/pids.c b/utils/pids.c
new file mode 100644
index 00000000..1f3ae316
--- /dev/null
+++ b/utils/pids.c
@@ -0,0 +1,232 @@
+/* Pid parsing/frobbing
+
+ Copyright (C) 1997, 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <argp.h>
+#include <hurd.h>
+#include <hurd/process.h>
+#include <mach.h>
+#include <sys/mman.h>
+
+#include "parse.h"
+#include "pids.h"
+
+static process_t _proc_server = MACH_PORT_NULL;
+
+/* Return this process's proc server. */
+static inline process_t
+proc_server ()
+{
+ if (_proc_server == MACH_PORT_NULL)
+ _proc_server = getproc ();
+ return _proc_server;
+}
+
+/* Add the pids returned in vm_allocated memory by calling PIDS_FN with ID as
+ an argument to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+error_t
+add_fn_pids (pid_t **pids, size_t *num_pids, unsigned id,
+ error_t (*pids_fn)(process_t proc, pid_t id,
+ pid_t **pids, unsigned *num_pids))
+{
+ size_t num_new_pids = 25;
+ pid_t _new_pids[num_new_pids], *new_pids = _new_pids;
+ error_t err = (*pids_fn)(proc_server (), id, &new_pids, &num_new_pids);
+
+ if (! err)
+ {
+ size_t new_sz = *num_pids + num_new_pids;
+ pid_t *new = realloc (*pids, new_sz * sizeof (pid_t));
+ if (new)
+ {
+ bcopy (new_pids, new + (*num_pids * sizeof (pid_t)),
+ num_new_pids * sizeof (pid_t));
+ *pids = new;
+ *num_pids = new_sz;
+ }
+ else
+ err = ENOMEM;
+ if (new_pids != _new_pids)
+ munmap (new_pids, num_new_pids * sizeof (pid_t));
+ }
+
+ return err;
+}
+
+/* Add PID to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+error_t
+add_pid (pid_t **pids, size_t *num_pids, pid_t pid)
+{
+ size_t new_sz = *num_pids + 1;
+ pid_t *new = realloc (*pids, new_sz * sizeof (pid_t));
+
+ if (new)
+ {
+ new[new_sz - 1] = pid;
+ *pids = new;
+ *num_pids = new_sz;
+ return 0;
+ }
+ else
+ return ENOMEM;
+}
+
+struct pids_parse_state
+{
+ struct pids_argp_params *params;
+ struct argp_state *state;
+};
+
+/* Returns our session id. */
+static pid_t
+current_sid (struct argp_state *state)
+{
+ pid_t sid = -1;
+ error_t err = proc_getsid (proc_server (), getpid (), &sid);
+ if (err)
+ argp_failure (state, 2, err, "Couldn't get current session id");
+ return sid;
+}
+
+/* Returns our login collection id. */
+static pid_t
+current_lid (struct argp_state *state)
+{
+ pid_t lid = -1;
+ error_t err = proc_getloginid (proc_server (), getpid (), &lid);
+ if (err)
+ argp_failure (state, 2, err, "Couldn't get current login collection");
+ return lid;
+}
+
+/* Add a specific process to be printed out. */
+static error_t
+parse_pid (unsigned pid, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err = add_pid (params->pids, params->num_pids, pid);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add process", pid);
+ return err;
+}
+
+/* Print out all process from the given session. */
+static error_t
+parse_sid (unsigned sid, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err =
+ add_fn_pids (params->pids, params->num_pids, sid, proc_getsessionpids);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add session", sid);
+ return err;
+}
+
+/* Print out all process from the given login collection. */
+static error_t
+parse_lid (unsigned lid, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err =
+ add_fn_pids (params->pids, params->num_pids, lid, proc_getloginpids);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add login collection", lid);
+ return err;
+}
+
+/* Print out all process from the given process group. */
+static error_t
+parse_pgrp (unsigned pgrp, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err =
+ add_fn_pids (params->pids, params->num_pids, pgrp, proc_getpgrppids);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add process group", pgrp);
+ return err;
+}
+
+#define OA OPTION_ARG_OPTIONAL
+
+/* Options for PIDS_ARGP. */
+static const struct argp_option options[] =
+{
+ {"login", 'L', "LID", OA, "Processes from the login"
+ " collection LID (which defaults that of"
+ " the current process)"},
+ {"lid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"pid", 'p', "PID", 0, "The process PID"},
+ {"pgrp", 'P', "PGRP", 0, "Processes in process group PGRP"},
+ {"session", 'S', "SID", OA, "Processes from the session SID"
+ " (which defaults to that of the"
+ " current process)"},
+ {"sid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {0, 0}
+};
+
+/* Parse one option/arg for PIDS_ARGP. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+
+ switch (key)
+ {
+ case 'p':
+ return
+ parse_numlist (arg, parse_pid, NULL, NULL, "process id", state);
+ case 'S':
+ return
+ parse_numlist (arg, parse_sid, current_sid, NULL, "session id", state);
+ case 'L':
+ return
+ parse_numlist (arg, parse_lid, current_lid, NULL, "login collection",
+ state);
+ case 'P':
+ return
+ parse_numlist (arg, parse_pgrp, NULL, NULL, "process group", state);
+
+ case ARGP_KEY_ARG:
+ if (params->parse_pid_args)
+ return parse_numlist (arg, parse_pid, NULL, NULL, "process id", state);
+ /* Else fall through */
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+}
+
+/* Filtering of help output strings for PIDS_ARGP. */
+static char *
+help_filter (int key, const char *text, void *input)
+{
+ struct pids_argp_params *params = input;
+
+ /* Describe the optional behavior of parsing normal args as pids. */
+ if (key == ARGP_KEY_HELP_ARGS_DOC && params->parse_pid_args)
+ return strdup ("[PID...]");
+
+ return (char *)text;
+}
+
+/* A parser for selecting a set of pids. */
+struct argp pids_argp = { options, parse_opt, 0, 0, 0, help_filter };
diff --git a/utils/pids.h b/utils/pids.h
new file mode 100644
index 00000000..8b192b5a
--- /dev/null
+++ b/utils/pids.h
@@ -0,0 +1,47 @@
+/* Pid parsing/frobbing
+
+ Copyright (C) 1997 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 __PIDS_H__
+#define __PIDS_H__
+
+/* Add the pids returned in vm_allocated memory by calling PIDS_FN with ID as
+ an argument to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+extern error_t add_fn_pids (pid_t **pids, size_t *num_pids, unsigned id,
+ error_t (*pids_fn)(process_t proc, pid_t id,
+ pid_t **pids, size_t *num_pids));
+
+/* Add PID to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+extern error_t add_pid (pid_t **pids, size_t *num_pids, pid_t pid);
+
+/* Params to be passed as the input when parsing PIDS_ARGP. */
+struct pids_argp_params
+{
+ /* Array to be extended with parsed pids. */
+ pid_t **pids;
+ size_t *num_pids;
+
+ /* If true, parse non-option arguments as pids. */
+ int parse_pid_args;
+};
+
+/* A parser for selecting a set of pids. */
+extern struct argp pids_argp;
+
+#endif __PIDS_H__
diff --git a/utils/ping.c b/utils/ping.c
new file mode 100644
index 00000000..2f979e2c
--- /dev/null
+++ b/utils/ping.c
@@ -0,0 +1,1156 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+char copyright[] =
+ "@(#) Copyright (c) 1989 The Regents of the University of California.\n"
+ "All rights reserved.\n";
+/*
+ * From: @(#)ping.c 5.9 (Berkeley) 5/12/91
+ */
+char rcsid[] = "$Id: ping.c,v 1.3 1998/10/20 09:42:49 roland Exp $";
+char pkg[] = "netkit-base-0.10";
+
+/*
+ * P I N G . C
+ *
+ * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
+ * measure round-trip-delays and packet loss across network paths.
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ *
+ * Status -
+ * Public Domain. Distribution Unlimited.
+ * Bugs -
+ * More statistics could always be gathered.
+ * This program has to run SUID to ROOT to access the ICMP socket.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <cthreads.h>
+/*
+ * Note: on some systems dropping root makes the process dumpable or
+ * traceable. In that case if you enable dropping root and someone
+ * traces ping, they get control of a raw socket and can start
+ * spoofing whatever packets they like. SO BE CAREFUL.
+ */
+#ifdef __linux__
+#define SAFE_TO_DROP_ROOT
+#endif
+
+#define DEFDATALEN (64 - 8) /* default data length */
+#define MAXIPLEN 60
+#define MAXICMPLEN 76
+#define MAXPACKET (65536 - 60 - 8)/* max packet size */
+#define MAXWAIT 10 /* max seconds to wait for response */
+#define NROUTES 9 /* number of record route slots */
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+/* various options */
+int options;
+#define F_FLOOD 0x001
+#define F_INTERVAL 0x002
+#define F_NUMERIC 0x004
+#define F_PINGFILLED 0x008
+#define F_QUIET 0x010
+#define F_RROUTE 0x020
+#define F_SO_DEBUG 0x040
+#define F_SO_DONTROUTE 0x080
+#define F_VERBOSE 0x100
+
+/* multicast options */
+int moptions;
+#define MULTICAST_NOLOOP 0x001
+#define MULTICAST_TTL 0x002
+#define MULTICAST_IF 0x004
+
+/*
+ * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
+ * number of received sequence numbers we can keep track of. Change 128
+ * to 8192 for complete accuracy...
+ */
+#define MAX_DUP_CHK (8 * 128)
+int mx_dup_ck = MAX_DUP_CHK;
+char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct sockaddr whereto; /* who to ping */
+int datalen = DEFDATALEN;
+int s; /* socket file descriptor */
+u_char outpack[MAXPACKET];
+char BSPACE = '\b'; /* characters written for flood */
+char DOT = '.';
+static char *hostname;
+static int ident; /* process id to identify our packets */
+
+/* counters */
+static long npackets; /* max packets to transmit */
+static long nreceived; /* # of packets we got back */
+static long nrepeats; /* number of duplicates */
+static long ntransmitted; /* sequence # for outbound packets = #sent */
+static int interval = 1; /* interval between packets */
+
+/* timing */
+static int timing; /* flag to do timing */
+static long tmin = LONG_MAX; /* minimum round trip time */
+static long tmax = 0; /* maximum round trip time */
+static u_long tsum; /* sum of all times, for doing average */
+
+/* protos */
+static char *pr_addr(u_long);
+static int in_cksum(u_short *addr, int len);
+static void catcher(int);
+static void finish(int ignore);
+static void pinger(void);
+static void fill(void *bp, char *patp);
+static void usage(void);
+static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
+static void tvsub(struct timeval *out, struct timeval *in);
+static void pr_icmph(struct icmp *icp);
+static void pr_retip(struct ip *ip);
+
+/* Only wrapper to call pinger (). */
+void *
+pinger_wrapper (void *ignored)
+{
+ for (;;)
+ {
+ pinger ();
+ sleep (interval);
+ }
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct timeval timeout;
+ struct hostent *hp;
+ struct sockaddr_in *to;
+ struct protoent *proto;
+ struct in_addr ifaddr;
+ int i;
+ int ch, fdmask, hold, packlen, preload;
+ u_char *datap, *packet;
+ char *target;
+ u_char ttl, loop;
+ int am_i_root;
+#ifdef IP_OPTIONS
+ char rspace[3 + 4 * NROUTES + 1]; /* record route space */
+#endif
+
+ static char *null = NULL;
+ __environ = &null;
+ am_i_root = (getuid()==0);
+
+ cthread_init ();
+
+ /*
+ * Pull this stuff up front so we can drop root if desired.
+ */
+ if (!(proto = getprotobyname("icmp"))) {
+ (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
+ exit(2);
+ }
+ if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
+ if (errno==EPERM) {
+ fprintf(stderr, "ping: ping must run as root\n");
+ }
+ else perror("ping: socket");
+ exit(2);
+ }
+
+#ifdef SAFE_TO_DROP_ROOT
+ setuid(getuid());
+#endif
+
+ preload = 0;
+ datap = &outpack[8 + sizeof(struct timeval)];
+ while ((ch = getopt(argc, argv, "I:LRc:dfh:i:l:np:qrs:t:v")) != EOF)
+ switch(ch) {
+ case 'c':
+ npackets = atoi(optarg);
+ if (npackets <= 0) {
+ (void)fprintf(stderr,
+ "ping: bad number of packets to transmit.\n");
+ exit(2);
+ }
+ break;
+ case 'd':
+ options |= F_SO_DEBUG;
+ break;
+ case 'f':
+ if (!am_i_root) {
+ (void)fprintf(stderr,
+ "ping: %s\n", strerror(EPERM));
+ exit(2);
+ }
+ options |= F_FLOOD;
+ setbuf(stdout, NULL);
+ break;
+ case 'i': /* wait between sending packets */
+ interval = atoi(optarg);
+ if (interval <= 0) {
+ (void)fprintf(stderr,
+ "ping: bad timing interval.\n");
+ exit(2);
+ }
+ options |= F_INTERVAL;
+ break;
+ case 'l':
+ if (!am_i_root) {
+ (void)fprintf(stderr,
+ "ping: %s\n", strerror(EPERM));
+ exit(2);
+ }
+ preload = atoi(optarg);
+ if (preload < 0) {
+ (void)fprintf(stderr,
+ "ping: bad preload value.\n");
+ exit(2);
+ }
+ break;
+ case 'n':
+ options |= F_NUMERIC;
+ break;
+ case 'p': /* fill buffer with user pattern */
+ options |= F_PINGFILLED;
+ fill(datap, optarg);
+ break;
+ case 'q':
+ options |= F_QUIET;
+ break;
+ case 'R':
+ options |= F_RROUTE;
+ break;
+ case 'r':
+ options |= F_SO_DONTROUTE;
+ break;
+ case 's': /* size of packet to send */
+ datalen = atoi(optarg);
+ if (datalen > MAXPACKET) {
+ (void)fprintf(stderr,
+ "ping: packet size too large.\n");
+ exit(2);
+ }
+ if (datalen <= 0) {
+ (void)fprintf(stderr,
+ "ping: illegal packet size.\n");
+ exit(2);
+ }
+ break;
+ case 'v':
+ options |= F_VERBOSE;
+ break;
+ case 'L':
+ moptions |= MULTICAST_NOLOOP;
+ loop = 0;
+ break;
+ case 't':
+ moptions |= MULTICAST_TTL;
+ i = atoi(optarg);
+ if (i < 0 || i > 255) {
+ printf("ttl %u out of range\n", i);
+ exit(2);
+ }
+ ttl = i;
+ break;
+ case 'I':
+ moptions |= MULTICAST_IF;
+ {
+ int i1, i2, i3, i4;
+ char junk;
+
+ if (sscanf(optarg, "%u.%u.%u.%u%c",
+ &i1, &i2, &i3, &i4, &junk) != 4) {
+ printf("bad interface address '%s'\n",
+ optarg);
+ exit(2);
+ }
+ ifaddr.s_addr = (i1<<24)|(i2<<16)|(i3<<8)|i4;
+ ifaddr.s_addr = htonl(ifaddr.s_addr);
+ }
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+ target = *argv;
+
+ memset(&whereto, 0, sizeof(struct sockaddr));
+ to = (struct sockaddr_in *)&whereto;
+ to->sin_family = AF_INET;
+ if (inet_aton(target, &to->sin_addr)) {
+ hostname = target;
+ }
+ else {
+ hp = gethostbyname(target);
+ if (!hp) {
+ (void)fprintf(stderr,
+ "ping: unknown host %s\n", target);
+ exit(2);
+ }
+ to->sin_family = hp->h_addrtype;
+ if (hp->h_length > (int)sizeof(to->sin_addr)) {
+ hp->h_length = sizeof(to->sin_addr);
+ }
+ memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
+ hostname = malloc (strlen (hp->h_name) + 1);
+ strcpy (hostname, hp->h_name);
+ }
+
+ if (options & F_FLOOD && options & F_INTERVAL) {
+ (void)fprintf(stderr,
+ "ping: -f and -i incompatible options.\n");
+ exit(2);
+ }
+
+ if (datalen >= (int)sizeof(struct timeval)) /* can we time transfer */
+ timing = 1;
+ packlen = datalen + MAXIPLEN + MAXICMPLEN;
+ packet = malloc((u_int)packlen);
+ if (!packet) {
+ (void)fprintf(stderr, "ping: out of memory.\n");
+ exit(2);
+ }
+ if (!(options & F_PINGFILLED))
+ for (i = 8; i < datalen; ++i)
+ *datap++ = i;
+
+ ident = getpid() & 0xFFFF;
+ hold = 1;
+
+ if (options & F_SO_DEBUG)
+ (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
+ sizeof(hold));
+
+ if (options & F_SO_DONTROUTE)
+ (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
+ sizeof(hold));
+
+ /* this is necessary for broadcast pings to work */
+ setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&hold, sizeof(hold));
+
+ /* record route option */
+ if (options & F_RROUTE) {
+#ifdef IP_OPTIONS
+ memset(rspace, 0, sizeof(rspace));
+ rspace[IPOPT_OPTVAL] = IPOPT_RR;
+ rspace[IPOPT_OLEN] = sizeof(rspace)-1;
+ rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
+ if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
+ sizeof(rspace)) < 0) {
+ perror("ping: record route");
+ exit(2);
+ }
+#else
+ (void)fprintf(stderr,
+ "ping: record route not available in this implementation.\n");
+ exit(2);
+#endif /* IP_OPTIONS */
+ }
+
+ /*
+ * When pinging the broadcast address, you can get a lot of answers.
+ * Doing something so evil is useful if you are trying to stress the
+ * ethernet, or just want to fill the arp cache to get some stuff for
+ * /etc/ethers.
+ */
+ hold = 48 * 1024;
+ (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
+ sizeof(hold));
+
+/*#if 0*/
+ if (moptions & MULTICAST_NOLOOP) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &loop, 1) == -1) {
+ perror ("can't disable multicast loopback");
+ exit(92);
+ }
+ }
+ if (moptions & MULTICAST_TTL) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, 1) == -1) {
+ perror ("can't set multicast time-to-live");
+ exit(93);
+ }
+ }
+ if (moptions & MULTICAST_IF) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ &ifaddr, sizeof(ifaddr)) == -1) {
+ perror ("can't set multicast source interface");
+ exit(94);
+ }
+ }
+/*#endif*/
+
+ if (to->sin_family == AF_INET)
+ (void)printf("PING %s (%s): %d data bytes\n", hostname,
+ inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
+ datalen);
+ else
+ (void)printf("PING %s: %d data bytes\n", hostname, datalen);
+
+ (void)signal(SIGINT, finish);
+
+ /* I replace this with thread -- Kunihiro
+ (void)signal(SIGALRM, catcher);
+ */
+
+
+ while (preload--) /* fire off them quickies */
+ pinger();
+
+ if ((options & F_FLOOD) == 0)
+ cthread_detach (cthread_fork (pinger_wrapper, NULL));
+
+
+ for (;;) {
+ struct sockaddr_in from;
+ register int cc;
+ size_t fromlen;
+
+ if (options & F_FLOOD) {
+ pinger();
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ fdmask = 1 << s;
+ if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
+ (fd_set *)NULL, &timeout) < 1)
+ continue;
+ }
+ fromlen = sizeof(from);
+ if ((cc = recvfrom(s, (char *)packet, packlen, 0,
+ (struct sockaddr *)&from, &fromlen)) < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("ping: recvfrom");
+ continue;
+ }
+ pr_pack((char *)packet, cc, &from);
+ if (npackets && nreceived >= npackets)
+ break;
+ }
+ finish(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+/*
+ * catcher --
+ * This routine causes another PING to be transmitted, and then
+ * schedules another SIGALRM for 1 second from now.
+ *
+ * bug --
+ * Our sense of time will slowly skew (i.e., packets will not be
+ * launched exactly at 1-second intervals). This does not affect the
+ * quality of the delay and loss statistics.
+ */
+static void
+catcher(int ignore)
+{
+ int waittime;
+
+ (void)ignore;
+ pinger();
+ (void)signal(SIGALRM, catcher);
+ if (!npackets || ntransmitted < npackets)
+ alarm((u_int)interval);
+ else {
+ if (nreceived) {
+ waittime = 2 * tmax / 1000;
+ if (!waittime)
+ waittime = 1;
+ if (waittime > MAXWAIT)
+ waittime = MAXWAIT;
+ } else
+ waittime = MAXWAIT;
+ (void)signal(SIGALRM, finish);
+ (void)alarm((u_int)waittime);
+ }
+}
+
+/*
+ * pinger --
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+static void
+pinger(void)
+{
+ register struct icmp *icp;
+ register int cc;
+ int i;
+
+ icp = (struct icmp *)outpack;
+ icp->icmp_type = ICMP_ECHO;
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = ntransmitted++;
+ icp->icmp_id = ident; /* ID */
+
+ CLR(icp->icmp_seq % mx_dup_ck);
+
+ if (timing)
+ (void)gettimeofday((struct timeval *)&outpack[8],
+ (struct timezone *)NULL);
+
+ cc = datalen + 8; /* skips ICMP portion */
+
+ /* compute ICMP checksum here */
+ icp->icmp_cksum = in_cksum((u_short *)icp, cc);
+
+ i = sendto(s, (char *)outpack, cc, 0, &whereto,
+ sizeof(struct sockaddr));
+
+ if (i < 0 || i != cc) {
+ if (i < 0)
+ perror("ping: sendto");
+ (void)printf("ping: wrote %s %d chars, ret=%d\n",
+ hostname, cc, i);
+ }
+ if (!(options & F_QUIET) && options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &DOT, 1);
+}
+
+/*
+ * pr_pack --
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+void
+pr_pack(char *buf, int cc, struct sockaddr_in *from)
+{
+ register struct icmp *icp;
+ register int i;
+ register u_char *cp,*dp;
+/*#if 0*/
+ register u_long l;
+ register int j;
+ static int old_rrlen;
+ static char old_rr[MAX_IPOPTLEN];
+/*#endif*/
+ struct ip *ip;
+ struct timeval tv, *tp;
+ long triptime = 0;
+ int hlen, dupflag;
+
+ (void)gettimeofday(&tv, (struct timezone *)NULL);
+
+ /* Check the IP header */
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < datalen + ICMP_MINLEN) {
+ if (options & F_VERBOSE)
+ (void)fprintf(stderr,
+ "ping: packet too short (%d bytes) from %s\n", cc,
+ inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
+ return;
+ }
+
+ /* Now the ICMP part */
+ cc -= hlen;
+ icp = (struct icmp *)(buf + hlen);
+ if (icp->icmp_type == ICMP_ECHOREPLY) {
+ if (icp->icmp_id != ident)
+ return; /* 'Twas not our ECHO */
+ ++nreceived;
+ if (timing) {
+#ifndef icmp_data
+ tp = (struct timeval *)(icp + 1);
+#else
+ tp = (struct timeval *)icp->icmp_data;
+#endif
+ tvsub(&tv, tp);
+ triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+
+ if (TST(icp->icmp_seq % mx_dup_ck)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(icp->icmp_seq % mx_dup_ck);
+ dupflag = 0;
+ }
+
+ if (options & F_QUIET)
+ return;
+
+ if (options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &BSPACE, 1);
+ else {
+ (void)printf("%d bytes from %s: icmp_seq=%u", cc,
+ inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
+ icp->icmp_seq);
+ (void)printf(" ttl=%d", ip->ip_ttl);
+ if (timing)
+ (void)printf(" time=%ld.%ld ms", triptime/10,
+ triptime%10);
+ if (dupflag)
+ (void)printf(" (DUP!)");
+ /* check the data */
+#ifndef icmp_data
+ cp = ((u_char*)(icp + 1) + 8);
+#else
+ cp = (u_char*)icp->icmp_data + 8;
+#endif
+ dp = &outpack[8 + sizeof(struct timeval)];
+ for (i = 8; i < datalen; ++i, ++cp, ++dp) {
+ if (*cp != *dp) {
+ (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
+ i, *dp, *cp);
+ cp = (u_char*)(icp + 1);
+ for (i = 8; i < datalen; ++i, ++cp) {
+ if ((i % 32) == 8)
+ (void)printf("\n\t");
+ (void)printf("%x ", *cp);
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ /* We've got something other than an ECHOREPLY */
+ if (!(options & F_VERBOSE))
+ return;
+ (void)printf("%d bytes from %s: ", cc,
+ pr_addr(from->sin_addr.s_addr));
+ pr_icmph(icp);
+ }
+
+/*#if 0*/
+ /* Display any IP options */
+ cp = (u_char *)buf + sizeof(struct ip);
+
+ for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
+ switch (*cp) {
+ case IPOPT_EOL:
+ hlen = 0;
+ break;
+ case IPOPT_LSRR:
+ (void)printf("\nLSRR: ");
+ hlen -= 2;
+ j = *++cp;
+ ++cp;
+ if (j > IPOPT_MINOFF)
+ for (;;) {
+ l = *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ if (l == 0)
+ (void)printf("\t0.0.0.0");
+ else
+ (void)printf("\t%s", pr_addr(ntohl(l)));
+ hlen -= 4;
+ j -= 4;
+ if (j <= IPOPT_MINOFF)
+ break;
+ (void)putchar('\n');
+ }
+ break;
+ case IPOPT_RR:
+ j = *++cp; /* get length */
+ i = *++cp; /* and pointer */
+ hlen -= 2;
+ if (i > j)
+ i = j;
+ i -= IPOPT_MINOFF;
+ if (i <= 0)
+ continue;
+ if (i == old_rrlen
+ && cp == (u_char *)buf + sizeof(struct ip) + 2
+ && !memcmp((char *)cp, old_rr, i)
+ && !(options & F_FLOOD)) {
+ (void)printf("\t(same route)");
+ i = ((i + 3) / 4) * 4;
+ hlen -= i;
+ cp += i;
+ break;
+ }
+ old_rrlen = i;
+ memcpy(old_rr, cp, i);
+ (void)printf("\nRR: ");
+ for (;;) {
+ l = *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ if (l == 0)
+ (void)printf("\t0.0.0.0");
+ else
+ (void)printf("\t%s", pr_addr(ntohl(l)));
+ hlen -= 4;
+ i -= 4;
+ if (i <= 0)
+ break;
+ (void)putchar('\n');
+ }
+ break;
+ case IPOPT_NOP:
+ (void)printf("\nNOP");
+ break;
+ default:
+ (void)printf("\nunknown option %x", *cp);
+ break;
+ }
+/*#endif*/
+ if (!(options & F_FLOOD)) {
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ }
+}
+
+/*
+ * in_cksum --
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+static int
+in_cksum(u_short *addr, int len)
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register int sum = 0;
+ u_short answer = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(u_char *)(&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return(answer);
+}
+
+/*
+ * tvsub --
+ * Subtract 2 timeval structs: out = out - in. Out is assumed to
+ * be >= in.
+ */
+static void
+tvsub(register struct timeval *out, register struct timeval *in)
+{
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * finish --
+ * Print out statistics, and give up.
+ */
+static void
+finish(int ignore)
+{
+ (void)ignore;
+
+ (void)signal(SIGINT, SIG_IGN);
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ (void)printf("--- %s ping statistics ---\n", hostname);
+ (void)printf("%ld packets transmitted, ", ntransmitted);
+ (void)printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ (void)printf("+%ld duplicates, ", nrepeats);
+ if (ntransmitted)
+ if (nreceived > ntransmitted)
+ (void)printf("-- somebody's printing up packets!");
+ else
+ (void)printf("%d%% packet loss",
+ (int) (((ntransmitted - nreceived) * 100) /
+ ntransmitted));
+ (void)putchar('\n');
+ if (nreceived && timing)
+ (void)printf("round-trip min/avg/max = %ld.%ld/%lu.%ld/%ld.%ld ms\n",
+ tmin/10, tmin%10,
+ (tsum / (nreceived + nrepeats))/10,
+ (tsum / (nreceived + nrepeats))%10,
+ tmax/10, tmax%10);
+
+ if (nreceived==0) exit(1);
+ exit(0);
+}
+
+#ifdef notdef
+static char *ttab[] = {
+ "Echo Reply", /* ip + seq + udata */
+ "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
+ "Source Quench", /* IP */
+ "Redirect", /* redirect type, gateway, + IP */
+ "Echo",
+ "Time Exceeded", /* transit, frag reassem + IP */
+ "Parameter Problem", /* pointer + IP */
+ "Timestamp", /* id + seq + three timestamps */
+ "Timestamp Reply", /* " */
+ "Info Request", /* id + sq */
+ "Info Reply" /* " */
+};
+#endif
+
+/*
+ * pr_icmph --
+ * Print a descriptive string about an ICMP header.
+ */
+static void
+pr_icmph(struct icmp *icp)
+{
+ switch(icp->icmp_type) {
+ case ICMP_ECHOREPLY:
+ (void)printf("Echo Reply\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP_DEST_UNREACH:
+ switch(icp->icmp_code) {
+ case ICMP_NET_UNREACH:
+ (void)printf("Destination Net Unreachable\n");
+ break;
+ case ICMP_HOST_UNREACH:
+ (void)printf("Destination Host Unreachable\n");
+ break;
+ case ICMP_PROT_UNREACH:
+ (void)printf("Destination Protocol Unreachable\n");
+ break;
+ case ICMP_PORT_UNREACH:
+ (void)printf("Destination Port Unreachable\n");
+ break;
+ case ICMP_FRAG_NEEDED:
+ (void)printf("frag needed and DF set\n");
+ break;
+ case ICMP_SR_FAILED:
+ (void)printf("Source Route Failed\n");
+ break;
+ case ICMP_NET_UNKNOWN:
+ (void)printf("Network Unknown\n");
+ break;
+ case ICMP_HOST_UNKNOWN:
+ (void)printf("Host Unknown\n");
+ break;
+ case ICMP_HOST_ISOLATED:
+ (void)printf("Host Isolated\n");
+ break;
+ case ICMP_NET_UNR_TOS:
+ printf("Destination Network Unreachable At This TOS\n");
+ break;
+ case ICMP_HOST_UNR_TOS:
+ printf("Destination Host Unreachable At This TOS\n");
+ break;
+#ifdef ICMP_PKT_FILTERED
+ case ICMP_PKT_FILTERED:
+ (void)printf("Packet Filtered\n");
+ break;
+#endif
+#ifdef ICMP_PREC_VIOLATION
+ case ICMP_PREC_VIOLATION:
+ (void)printf("Precedence Violation\n");
+ break;
+#endif
+#ifdef ICMP_PREC_CUTOFF
+ case ICMP_PREC_CUTOFF:
+ (void)printf("Precedence Cutoff\n");
+ break;
+#endif
+ default:
+ (void)printf("Dest Unreachable, Unknown Code: %d\n",
+ icp->icmp_code);
+ break;
+ }
+ /* Print returned IP header information */
+#ifndef icmp_data
+ pr_retip((struct ip *)(icp + 1));
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_SOURCE_QUENCH:
+ (void)printf("Source Quench\n");
+#ifndef icmp_data
+ pr_retip((struct ip *)(icp + 1));
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_REDIRECT:
+ switch(icp->icmp_code) {
+ case ICMP_REDIR_NET:
+ (void)printf("Redirect Network");
+ break;
+ case ICMP_REDIR_HOST:
+ (void)printf("Redirect Host");
+ break;
+ case ICMP_REDIR_NETTOS:
+ (void)printf("Redirect Type of Service and Network");
+ break;
+ case ICMP_REDIR_HOSTTOS:
+ (void)printf("Redirect Type of Service and Host");
+ break;
+ default:
+ (void)printf("Redirect, Bad Code: %d", icp->icmp_code);
+ break;
+ }
+ (void)printf("(New addr: %s)\n",
+ inet_ntoa(icp->icmp_gwaddr));
+#ifndef icmp_data
+ pr_retip((struct ip *)(icp + 1));
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_ECHO:
+ (void)printf("Echo Request\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP_TIME_EXCEEDED:
+ switch(icp->icmp_code) {
+ case ICMP_EXC_TTL:
+ (void)printf("Time to live exceeded\n");
+ break;
+ case ICMP_EXC_FRAGTIME:
+ (void)printf("Frag reassembly time exceeded\n");
+ break;
+ default:
+ (void)printf("Time exceeded, Bad Code: %d\n",
+ icp->icmp_code);
+ break;
+ }
+#ifndef icmp_data
+ pr_retip((struct ip *)(icp + 1));
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_PARAMETERPROB:
+ (void)printf("Parameter problem: IP address = %s\n",
+ inet_ntoa (icp->icmp_gwaddr));
+#ifndef icmp_data
+ pr_retip((struct ip *)(icp + 1));
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_TIMESTAMP:
+ (void)printf("Timestamp\n");
+ /* XXX ID + Seq + 3 timestamps */
+ break;
+ case ICMP_TIMESTAMPREPLY:
+ (void)printf("Timestamp Reply\n");
+ /* XXX ID + Seq + 3 timestamps */
+ break;
+ case ICMP_INFO_REQUEST:
+ (void)printf("Information Request\n");
+ /* XXX ID + Seq */
+ break;
+ case ICMP_INFO_REPLY:
+ (void)printf("Information Reply\n");
+ /* XXX ID + Seq */
+ break;
+#ifdef ICMP_MASKREQ
+ case ICMP_MASKREQ:
+ (void)printf("Address Mask Request\n");
+ break;
+#endif
+#ifdef ICMP_MASKREPLY
+ case ICMP_MASKREPLY:
+ (void)printf("Address Mask Reply\n");
+ break;
+#endif
+ default:
+ (void)printf("Bad ICMP type: %d\n", icp->icmp_type);
+ }
+}
+
+/*
+ * pr_iph --
+ * Print an IP header with options.
+ */
+static void
+pr_iph(struct ip *ip)
+{
+ int hlen;
+ u_char *cp;
+
+ hlen = ip->ip_hl << 2;
+ cp = (u_char *)ip + 20; /* point to options */
+
+ (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
+ (void)printf(" %1x %1x %02x %04x %04x",
+ ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
+ (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
+ (ip->ip_off) & 0x1fff);
+ (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
+ (void)printf(" %s ", inet_ntoa(*((struct in_addr *) &ip->ip_src)));
+ (void)printf(" %s ", inet_ntoa(*((struct in_addr *) &ip->ip_dst)));
+ /* dump and option bytes */
+ while (hlen-- > 20) {
+ (void)printf("%02x", *cp++);
+ }
+ (void)putchar('\n');
+}
+
+/*
+ * pr_addr --
+ * Return an ascii host address as a dotted quad and optionally with
+ * a hostname.
+ */
+static char *
+pr_addr(u_long l)
+{
+ struct hostent *hp;
+ static char buf[256];
+
+ if ((options & F_NUMERIC) ||
+ !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
+ (void)snprintf(buf, sizeof(buf), "%s",
+ inet_ntoa(*(struct in_addr *)&l));
+ else
+ (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
+ inet_ntoa(*(struct in_addr *)&l));
+ return(buf);
+}
+
+/*
+ * pr_retip --
+ * Dump some info on a returned (via ICMP) IP packet.
+ */
+static void
+pr_retip(struct ip *ip)
+{
+ int hlen;
+ u_char *cp;
+
+ pr_iph(ip);
+ hlen = ip->ip_hl << 2;
+ cp = (u_char *)ip + hlen;
+
+ if (ip->ip_p == 6)
+ (void)printf("TCP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
+ else if (ip->ip_p == 17)
+ (void)printf("UDP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
+}
+
+static void
+fill(void *bp1, char *patp)
+{
+ register int ii, jj, kk;
+ int pat[16];
+ char *cp, *bp = (char *)bp1;
+
+ for (cp = patp; *cp; cp++)
+ if (!isxdigit(*cp)) {
+ (void)fprintf(stderr,
+ "ping: patterns must be specified as hex digits.\n");
+ exit(2);
+ }
+ ii = sscanf(patp,
+ "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
+ &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
+ &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
+ &pat[13], &pat[14], &pat[15]);
+
+ if (ii > 0)
+ for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii)
+ for (jj = 0; jj < ii; ++jj)
+ bp[jj + kk] = pat[jj];
+ if (!(options & F_QUIET)) {
+ (void)printf("PATTERN: 0x");
+ for (jj = 0; jj < ii; ++jj)
+ (void)printf("%02x", bp[jj] & 0xFF);
+ (void)printf("\n");
+ }
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: ping [-LRdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] [-t ttl] [-I interface address] host\n");
+ exit(2);
+}
diff --git a/utils/portinfo.c b/utils/portinfo.c
new file mode 100644
index 00000000..d5bdadf0
--- /dev/null
+++ b/utils/portinfo.c
@@ -0,0 +1,226 @@
+/* Print information about a task's ports
+
+ Copyright (C) 1996,97,98,99 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 <stddef.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <version.h>
+
+#include <mach.h>
+
+#include <hurd.h>
+#include <hurd/process.h>
+
+#include <portinfo.h>
+#include <portxlate.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (portinfo);
+
+static const struct argp_option options[] = {
+ {0,0,0,0,0, 1},
+ {"verbose", 'v', 0, 0, "Give more detailed information"},
+ {"members", 'm', 0, 0, "Show members of port-sets"},
+ {"hex-names", 'x', 0, 0, "Show port names in hexadecimal"},
+#if 0 /* XXX implement this */
+ {"query-process", 'q', 0, 0, "Query the process itself for the identity of"
+ " the ports in question -- requires the process be in a sane state"},
+#endif
+ {"hold", '*', 0, OPTION_HIDDEN},
+
+ {0,0,0,0, "Selecting which names to show:", 2},
+ {"receive", 'r', 0, 0, "Show ports with receive rights"},
+ {"send", 's', 0, 0, "Show ports with send rights"},
+ {"send-once", 'o', 0, 0, "Show ports with send once rights"},
+ {"dead-names",'d', 0, 0, "Show dead names"},
+ {"port-sets", 'p', 0, 0, "Show port sets"},
+
+ {0,0,0,0, "Translating port names between tasks:", 3},
+ {"translate", 't', "PID", 0, "Translate port names to process PID"},
+ {"show-targets", 'h', 0, 0,
+ "Print a header describing the target process" },
+ {"no-translation-errors", 'E', 0, 0,
+ "Don't display an error if a specified port can't be translated" },
+#if 0
+ {"search", 'a', 0, 0, "Search all processes for the given ports"},
+ {"target-receive", 'R', 0, 0,
+ "Only show ports that translate into receive rights"},
+ {"target-send", 'S', 0, 0,
+ "Only show ports that translate into send rights"},
+ {"target-send-once",'O', 0, 0,
+ "Only show ports that translate into send-once rights"},
+ "Only show ports that translate into send once rights"},
+#endif
+
+ {0}
+};
+static const char *args_doc = "PID [NAME...]";
+static const char *doc =
+"Show information about mach ports NAME... (default all ports) in process PID."
+"\vIf no port NAMEs are given, all ports in process PID are reported (if"
+" translation is used, then only those common to both processes). NAMEs"
+" may be specified in hexadecimal or octal by using a 0x or 0 prefix.";
+
+/* Return the task corresponding to the user argument ARG, exiting with an
+ appriate error message if we can't. */
+static task_t
+parse_task (char *arg)
+{
+ error_t err;
+ task_t task;
+ char *arg_end;
+ pid_t pid = strtoul (arg, &arg_end, 10);
+ static process_t proc = MACH_PORT_NULL;
+
+ if (*arg == '\0' || *arg_end != '\0')
+ error (10, 0, "%s: Invalid process id", arg);
+
+ if (proc == MACH_PORT_NULL)
+ proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ error (11, err, "%s", arg);
+ else if (task == MACH_PORT_NULL)
+ error (11, 0, "%s: Process %d is dead and has no task", arg, (int) pid);
+
+ return task;
+}
+
+static volatile int hold = 0;
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ task_t task;
+ int search = 0;
+ unsigned show = 0; /* what info we print */
+ mach_port_type_t only = 0, target_only = 0; /* Which names to show */
+ task_t xlate_task = MACH_PORT_NULL;
+ int no_translation_errors = 0; /* inhibit complaints about bad names */
+ struct port_name_xlator *xlator = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'v': show |= PORTINFO_DETAILS; break;
+ case 'm': show |= PORTINFO_MEMBERS; break;
+ case 'x': show |= PORTINFO_HEX_NAMES; break;
+
+ case 'r': only |= MACH_PORT_TYPE_RECEIVE; break;
+ case 's': only |= MACH_PORT_TYPE_SEND; break;
+ case 'o': only |= MACH_PORT_TYPE_SEND_ONCE; break;
+ case 'd': only |= MACH_PORT_TYPE_DEAD_NAME; break;
+ case 'p': only |= MACH_PORT_TYPE_PORT_SET; break;
+
+ case 'R': target_only |= MACH_PORT_TYPE_RECEIVE; break;
+ case 'S': target_only |= MACH_PORT_TYPE_SEND; break;
+ case 'O': target_only |= MACH_PORT_TYPE_SEND_ONCE; break;
+
+ case 't': xlate_task = parse_task (arg); break;
+ case 'a': search = 1; break;
+ case 'E': no_translation_errors = 1; break;
+
+ case '*':
+ hold = 1;
+ while (hold)
+ sleep (1);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ /* The task */
+ {
+ task = parse_task (arg);
+
+ if (only == 0)
+ only = ~0;
+ if (target_only == 0)
+ target_only = ~0;
+
+ if (xlate_task != MACH_PORT_NULL)
+ {
+ if (search)
+ argp_error (state,
+ "Both --search and --translate specified");
+ err = port_name_xlator_create (task, xlate_task, &xlator);
+ if (err)
+ error (13, err, "Cannot setup task translation");
+ }
+
+ if (state->next == state->argc)
+ /* No port names specified, print all of them. */
+ {
+ if (xlator)
+ err = print_xlated_task_ports_info (xlator, only,
+ show, stdout);
+ else
+ err = print_task_ports_info (task, only, show, stdout);
+ if (err)
+ error (12, err, "%s", arg);
+ }
+ break;
+ }
+
+ /* A port name */
+ {
+ char *end;
+ mach_port_t name = strtoul (arg, &end, 0);
+ if (name == 0)
+ error (0, 0, "%s: Invalid port name", arg);
+ else
+ {
+ if (xlator)
+ {
+ err = print_xlated_port_info (name, 0, xlator,
+ show, stdout);
+ if (err && no_translation_errors)
+ break;
+ }
+ else
+ err = print_port_info (name, 0, task, show, stdout);
+ if (err)
+ error (0, err, "%s", arg);
+ }
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ exit (0);
+}
diff --git a/utils/ps.c b/utils/ps.c
new file mode 100644
index 00000000..3dba399e
--- /dev/null
+++ b/utils/ps.c
@@ -0,0 +1,442 @@
+/* Show process information.
+
+ Copyright (C) 1995,96,97,98,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 <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <argp.h>
+#include <argz.h>
+#include <idvec.h>
+#include <ps.h>
+#include <error.h>
+#include <version.h>
+
+#include "psout.h"
+#include "parse.h"
+#include "pids.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ps);
+
+#define OA OPTION_ARG_OPTIONAL
+
+static const struct argp_option options[] =
+{
+ {0,0,0,0, "Output format selection:", 1},
+ {"format", 'F', "FMT", 0, "Use the output-format FMT; FMT may be"
+ " `default', `user', `vmem', `long',"
+ " `jobc', `full', `hurd', `hurd-long',"
+ " or a custom format-string"},
+ {"posix-format",'o', "FMT", 0, "Use the posix-style output-format FMT"},
+ {0, 'f', 0, 0, "Use the `full' output-format"},
+ {0, 'j', 0, 0, "Use the `jobc' output-format"},
+ {0, 'l', 0, 0, "Use the `long' output-format"},
+ {0, 'u', 0, 0, "Use the `user' output-format"},
+ {0, 'v', 0, 0, "Use the `vmem' output-format"},
+
+ {0,0,0,0, "Process filtering (by default, other users'"
+ " processes, threads, and process-group leaders are not shown):", 2},
+ {"all-users", 'a', 0, 0, "List other users' processes"},
+ {0, 'd', 0, 0, "List all processes except process group"
+ " leaders"},
+ {"all", 'e', 0, 0, "List all processes"},
+ {0, 'A', 0, OPTION_ALIAS}, /* Posix option meaning -e */
+ {0, 'g', 0, 0, "Include session and login leaders"},
+ {"owner", 'U', "USER", 0, "Show only processes owned by USER"},
+ {"not-owner", 'O', "USER", 0, "Show only processes not owned by USER"},
+ {"no-parent", 'P', 0, 0, "Include processes without parents"},
+ {"threads", 'T', 0, 0, "Show the threads for each process"},
+ {"tty", 't', "TTY", OA, "Only show processes with controlling"
+ " terminal TTY"},
+ {0, 'x', 0, 0, "Include orphaned processes"},
+
+ {0,0,0,0, "Elision of output fields:", 4},
+ {"no-msg-port",'M', 0, 0, "Don't show info that uses a process's"
+ " msg port"},
+ {"nominal-fields",'n', 0, 0, "Don't elide fields containing"
+ " `uninteresting' data"},
+ {"all-fields", 'Q', 0, 0, "Don't elide unusable fields (normally"
+ " if there's some reason ps can't print"
+ " a field for any process, it's removed"
+ " from the output entirely)"},
+
+ {0,0,0,0, "Output attributes:"},
+ {"no-header", 'H', 0, 0, "Don't print a descriptive header line"},
+ {"reverse", 'r', 0, 0, "Reverse the order of any sort"},
+ {"sort", 's', "FIELD",0, "Sort the output with respect to FIELD,"
+ " backwards if FIELD is prefixed by `-'"},
+ {"top", 'h', "ENTRIES", OA, "Show the top ENTRIES processes"
+ " (default 10), or if ENTRIES is"
+ " negative, the bottom -ENTRIES"},
+ {"head", 0, 0, OPTION_ALIAS},
+ {"bottom", 'b', "ENTRIES", OA, "Show the bottom ENTRIES processes"
+ " (default 10)"},
+ {"tail", 0, 0, OPTION_ALIAS},
+ {"width", 'w', "WIDTH",OA, "If WIDTH is given, try to format the"
+ " output for WIDTH columns, otherwise,"
+ " remove the default limit"},
+ {0, 0}
+};
+
+static const char doc[] =
+"Show information about processes PID... (default all `interesting' processes)"
+"\vThe USER, LID, PID, PGRP, and SID arguments may also be comma separated"
+" lists. The System V options -u and -g may be accessed with -O and -G.";
+
+#define FILTER_OWNER 0x01
+#define FILTER_NOT_LEADER 0x02
+#define FILTER_CTTY 0x04
+#define FILTER_UNORPHANED 0x08
+#define FILTER_PARENTED 0x10
+
+/* A particular predefined output format. */
+struct output_fmt
+{
+ const char *name;
+ const char *sort_key; /* How this format should be sorted. */
+ const char *fmt; /* The format string. */
+};
+
+/* The predefined output formats. */
+struct output_fmt output_fmts[] =
+{
+ { "default", "pid",
+ "%^%?user %pid %th %tt %sc %stat %time %command" },
+ { "user", "-cpu",
+ "%^%user %pid %th %cpu %mem %sz %rss %tt %sc %stat %start %time %command" },
+ { "vmem", "-mem",
+ "%^%pid %th %stat %sl %pgins %pgflts %cowflts %zfills %sz %rss %cpu %mem %command"
+ },
+ { "long", "pid",
+ "%^%uid %pid %th %ppid %pri %ni %nth %msgi %msgo %sz %rss %sc %wait %stat %tt %time %command" },
+ { "jobc", "pid",
+ "%^%user %pid %th %ppid %pgrp %sess %lcoll %sc %stat %tt %time %command" },
+ { "full", "pid",
+ "%^%-user %pid %ppid %tty %time %command" },
+ { "hurd", "pid",
+ "%pid %th %uid %nth %{vsize:Vmem} %rss %{utime:User} %{stime:System} %args"
+ },
+ { "hurd-long", "pid",
+ "%pid %th %uid %ppid %pgrp %sess %nth %{vsize:Vmem} %rss %cpu %{utime:User} %{stime:System} %args"
+ }
+};
+
+/* Augment the standard specs with our own abbrevs. */
+static const struct ps_fmt_spec
+spec_abbrevs[] = {
+ {"TT=tty"}, {"SC=susp"}, {"Stat=state"}, {"Command=args"}, {"SL=sleep"},
+ {"NTH=nth", "TH"}, {"NI=bpri"}, {"SZ=vsize"}, {"RSS=rsize"},
+ {"MsgI=msgin"}, {"MsgO=msgout"},
+ {0}
+};
+static struct ps_fmt_specs ps_specs =
+ { spec_abbrevs, &ps_std_fmt_specs };
+
+/* Returns the UID for the user called NAME. */
+static int
+lookup_user (const char *name, struct argp_state *state)
+{
+ struct passwd *pw = getpwnam(name);
+ if (pw == NULL)
+ argp_failure (state, 2, 0, "%s: Unknown user", name);
+ return pw->pw_uid;
+}
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ /* A buffer used for rewriting old-style ps command line arguments that
+ need a dash prepended for the parser to understand them. It gets
+ realloced for each successive arg that needs it, on the assumption that
+ args don't get parsed multiple times. */
+ char *arg_hack_buf = 0;
+ struct idvec *only_uids = make_idvec (), *not_uids = make_idvec ();
+ char *tty_names = 0;
+ unsigned num_tty_names = 0;
+ struct proc_stat_list *procset;
+ struct ps_context *context;
+ const char *fmt_string = "default", *sort_key_name = NULL;
+ unsigned filter_mask =
+ FILTER_OWNER | FILTER_NOT_LEADER | FILTER_UNORPHANED | FILTER_PARENTED;
+ int sort_reverse = FALSE, print_heading = TRUE;
+ int squash_bogus_fields = TRUE, squash_nominal_fields = TRUE;
+ int show_threads = FALSE, no_msg_port = FALSE;
+ int output_width = -1; /* Desired max output size. */
+ int show_non_hurd_procs = 1; /* Show non-hurd processes. */
+ int posix_fmt = 0; /* Use a posix_fmt-style format string. */
+ int top = 0; /* Number of entries to output. */
+ pid_t *pids = 0; /* User-specified pids. */
+ size_t num_pids = 0;
+ struct pids_argp_params pids_argp_params = { &pids, &num_pids, 1 };
+
+ /* Add a user who's processes should be printed out. */
+ error_t add_uid (uid_t uid, struct argp_state *state)
+ {
+ error_t err = idvec_add (only_uids, uid);
+ if (err)
+ argp_failure (state, 23, err, "Can't add uid");
+ return err;
+ }
+ /* Add a user who's processes should not be printed out. */
+ error_t add_not_uid (uid_t uid, struct argp_state *state)
+ {
+ error_t err = idvec_add (not_uids, uid);
+ if (err)
+ argp_failure (state, 23, err, "Can't add uid");
+ return err;
+ }
+ /* Returns TRUE if PS is owned by any of the users in ONLY_UIDS, and none
+ in NOT_UIDS. */
+ int proc_stat_owner_ok(struct proc_stat *ps)
+ {
+ int uid = proc_stat_owner_uid (ps);
+ if (only_uids->num > 0 && !idvec_contains (only_uids, uid))
+ return 0;
+ if (not_uids->num > 0 && idvec_contains (not_uids, uid))
+ return 0;
+ return 1;
+ }
+
+ /* Add TTY_NAME to the list for which processes with those controlling
+ terminals will be printed. */
+ error_t add_tty_name (const char *tty_name, struct argp_state *state)
+ {
+ error_t err = argz_add (&tty_names, &num_tty_names, tty_name);
+ if (err)
+ argp_failure (state, 8, err, "%s: Can't add tty", tty_name);
+ return err;
+ }
+ int proc_stat_has_ctty(struct proc_stat *ps)
+ {
+ if (proc_stat_has(ps, PSTAT_TTY))
+ /* Only match processes whose tty we can figure out. */
+ {
+ struct ps_tty *tty = proc_stat_tty (ps);
+ if (tty)
+ {
+ char *try = 0;
+ const char *name = ps_tty_name (tty);
+ const char *short_name = ps_tty_short_name(tty);
+
+ while ((try = argz_next (tty_names, num_tty_names, try)))
+ if ((name && strcmp (try, name) == 0)
+ || (short_name && strcmp (try, short_name) == 0))
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ /* Returns the name of the current controlling terminal. */
+ static const char *current_tty_name()
+ {
+ error_t err;
+ struct ps_tty *tty;
+ mach_port_t cttyid = getcttyid();
+
+ if (cttyid == MACH_PORT_NULL)
+ error(2, 0, "No controlling terminal");
+
+ err = ps_context_find_tty_by_cttyid (context, cttyid, &tty);
+ if (err)
+ error(2, err, "Can't get controlling terminal");
+
+ return ps_tty_name (tty);
+ }
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG: /* Non-option argument. */
+ if (!isdigit (*arg) && !state->quoted)
+ /* Old-fashioned `ps' syntax takes options without the leading
+ dash. Prepend a dash and feed back to getopt. */
+ {
+ size_t len = strlen (arg) + 1;
+ arg_hack_buf = realloc (arg_hack_buf, 1 + len);
+ state->argv[--state->next] = arg_hack_buf;
+ state->argv[state->next][0] = '-';
+ memcpy (&state->argv[state->next][1], arg, len);
+ break;
+ }
+ else
+ /* Let PIDS_ARGP handle it. */
+ return ARGP_ERR_UNKNOWN;
+
+ case 'a': filter_mask &= ~FILTER_OWNER; break;
+ case 'd': filter_mask &= ~(FILTER_OWNER | FILTER_UNORPHANED); break;
+ case 'e': case 'A': filter_mask = 0; break;
+ case 'g': filter_mask &= ~FILTER_NOT_LEADER; break;
+ case 'x': filter_mask &= ~FILTER_UNORPHANED; break;
+ case 'P': filter_mask &= ~FILTER_PARENTED; break;
+ case 'f': fmt_string = "full"; break;
+ case 'u': fmt_string = "user"; break;
+ case 'v': fmt_string = "vmem"; break;
+ case 'j': fmt_string = "jobc"; break;
+ case 'l': fmt_string = "long"; break;
+ case 'M': no_msg_port = TRUE; break;
+ case 'H': print_heading = FALSE; break;
+ case 'Q': squash_bogus_fields = squash_nominal_fields = FALSE; break;
+ case 'n': squash_nominal_fields = FALSE; break;
+ case 'T': show_threads = TRUE; break;
+ case 's': sort_key_name = arg; break;
+ case 'r': sort_reverse = TRUE; break;
+ case 'h': top = arg ? atoi (arg) : 10; break;
+ case 'b': top = -(arg ? atoi (arg) : 10); break;
+ case 'F': fmt_string = arg; posix_fmt = 0; break;
+ case 'o': fmt_string = arg; posix_fmt = 1; break;
+
+ case 'w':
+ output_width = arg ? atoi (arg) : 0; /* 0 means `unlimited'. */
+ break;
+
+ case 't':
+ return parse_strlist (arg, add_tty_name, current_tty_name, "tty", state);
+ case 'U':
+ return parse_numlist (arg, add_uid, NULL, lookup_user, "user", state);
+ case 'O':
+ return parse_numlist (arg, add_not_uid, NULL, lookup_user, "user", state);
+
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ state->child_inputs[0] = &pids_argp_params;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp_child argp_kids[] =
+ { { &pids_argp, 0,
+ "Process selection (before filtering; default is all processes):", 3},
+ {0} };
+ struct argp argp = { options, parse_opt, 0, doc, argp_kids };
+
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ error(1, err, "ps_context_create");
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ /* Select an explicit format string if FMT_STRING is a format name. */
+ {
+ const char *fmt_name (unsigned n)
+ {
+ return
+ n >= (sizeof output_fmts / sizeof *output_fmts)
+ ? 0
+ : output_fmts[n].name;
+ }
+ int fmt_index = parse_enum (fmt_string, fmt_name, "format type", 1, 0);
+ if (fmt_index >= 0)
+ {
+ fmt_string = output_fmts[fmt_index].fmt;
+ if (sort_key_name == NULL)
+ sort_key_name = output_fmts[fmt_index].sort_key;
+ }
+ }
+
+ err = proc_stat_list_create(context, &procset);
+ if (err)
+ error(1, err, "proc_stat_list_create");
+
+ if (num_pids == 0)
+ /* No explicit processes specified. */
+ {
+ err = proc_stat_list_add_all (procset, 0, 0);
+
+ /* Try to avoid showing non-hurd processes if this isn't a native-booted
+ hurd system (because there would be lots of them). Here we use a
+ simple heuristic: Is the 5th process a hurd process (1-4 are
+ typically: proc server, init, kernel, boot default pager (maybe); the
+ last two are know not to be hurd processes)? */
+ if (procset->num_procs > 4)
+ {
+ struct proc_stat *ps = procset->proc_stats[4];
+ if (proc_stat_set_flags (ps, PSTAT_STATE) == 0
+ && (ps->flags & PSTAT_STATE))
+ show_non_hurd_procs = !(ps->state & PSTAT_STATE_P_NOPARENT);
+ else
+ /* Something is fucked, we can't get the state bits for PS.
+ Default to showing everything. */
+ show_non_hurd_procs = 1;
+ }
+ }
+ else
+ /* User-specified processes. */
+ {
+ err = proc_stat_list_add_pids (procset, pids, num_pids, 0);
+ filter_mask = 0; /* Don't mess with them. */
+ }
+
+ if (err)
+ error(2, err, "Can't get process list");
+
+ if (no_msg_port)
+ proc_stat_list_set_flags(procset, PSTAT_NO_MSGPORT);
+
+ if (only_uids->num == 0 && (filter_mask & FILTER_OWNER))
+ /* Restrict the output to only our own processes. */
+ {
+ int uid = getuid ();
+ if (uid >= 0)
+ add_uid (uid, 0);
+ else
+ filter_mask &= ~FILTER_OWNER; /* Must be an anonymous process. */
+ }
+
+ /* Filter out any processes that we don't want to show. */
+ if (only_uids->num || not_uids->num)
+ proc_stat_list_filter1 (procset, proc_stat_owner_ok,
+ PSTAT_OWNER_UID, FALSE);
+ if (num_tty_names > 0)
+ {
+ /* We set the PSTAT_TTY flag separately so that our filter function
+ can look at any procs that fail to set it. */
+ proc_stat_list_set_flags(procset, PSTAT_TTY);
+ proc_stat_list_filter1(procset, proc_stat_has_ctty, 0, FALSE);
+ }
+ if (filter_mask & FILTER_NOT_LEADER)
+ proc_stat_list_filter (procset, &ps_not_leader_filter, FALSE);
+ if (filter_mask & FILTER_UNORPHANED)
+ proc_stat_list_filter (procset, &ps_unorphaned_filter, FALSE);
+ if (!show_non_hurd_procs && (filter_mask & FILTER_PARENTED))
+ proc_stat_list_filter (procset, &ps_parent_filter, FALSE);
+
+ if (show_threads)
+ proc_stat_list_add_threads(procset);
+
+ proc_stat_list_filter (procset, &ps_alive_filter, FALSE);
+
+ psout (procset, fmt_string, posix_fmt, &ps_specs,
+ sort_key_name, sort_reverse,
+ output_width, print_heading,
+ squash_bogus_fields, squash_nominal_fields, top);
+
+ return 0;
+}
diff --git a/utils/psout.c b/utils/psout.c
new file mode 100644
index 00000000..d3bde0cf
--- /dev/null
+++ b/utils/psout.c
@@ -0,0 +1,144 @@
+/* Common output function for ps & w
+
+ Copyright (C) 1995, 1996, 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 <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <error.h>
+#include <ps.h>
+
+void
+psout (struct proc_stat_list *procs,
+ char *fmt_string, int posix_fmt, struct ps_fmt_specs *specs,
+ char *sort_key_name, int sort_reverse,
+ int output_width, int print_heading,
+ int squash_bogus_fields, int squash_nominal_fields,
+ int top)
+{
+ error_t err;
+ struct ps_stream *output;
+ struct ps_fmt *fmt;
+
+ err = ps_fmt_create (fmt_string, posix_fmt, specs, &fmt);
+ if (err)
+ {
+ char *problem;
+ ps_fmt_creation_error (fmt_string, posix_fmt, specs, &problem);
+ error (4, 0, "%s", problem);
+ }
+
+ if (squash_bogus_fields)
+ /* Remove any fields that we can't print anyway (because of system
+ bugs/protection violations?). */
+ {
+ ps_flags_t bogus_flags = ps_fmt_needs (fmt);
+
+ err = proc_stat_list_find_bogus_flags (procs, &bogus_flags);
+ if (err)
+ error (0, err, "Couldn't remove bogus fields");
+ else
+ ps_fmt_squash_flags (fmt, bogus_flags);
+ }
+
+ if (squash_nominal_fields)
+ /* Remove any fields that contain only `uninteresting' information. */
+ {
+ int nominal (struct ps_fmt_field *field)
+ {
+ return !(field->flags & PS_FMT_FIELD_KEEP)
+ && proc_stat_list_spec_nominal (procs, field->spec);
+ }
+ ps_fmt_squash (fmt, nominal);
+ }
+
+ if (sort_key_name)
+ /* Sort on the given field. */
+ {
+ const struct ps_fmt_spec *sort_key;
+
+ if (*sort_key_name == '-')
+ /* Sort in reverse. */
+ {
+ sort_reverse = 1;
+ sort_key_name++;
+ }
+
+ sort_key = ps_fmt_specs_find (specs, sort_key_name);
+ if (sort_key == NULL)
+ error (3, 0, "%s: bad sort key", sort_key_name);
+
+ err = proc_stat_list_sort (procs, sort_key, sort_reverse);
+ if (err)
+ /* Give an error message, but don't exit. */
+ error (0, err, "Couldn't sort processes");
+ }
+
+ err = ps_stream_create (stdout, &output);
+ if (err)
+ error (5, err, "Can't make output stream");
+
+ if (print_heading)
+ {
+ if (procs->num_procs > 0)
+ {
+ err = ps_fmt_write_titles (fmt, output);
+ if (err)
+ error (0, err, "Can't print titles");
+ ps_stream_newline (output);
+ }
+ else
+ error (0, 0, "No applicable processes");
+ }
+
+ if (output_width)
+ /* Try and restrict the number of output columns. */
+ {
+ int deduce_term_size (int fd, char *type, int *width, int *height);
+ if (output_width < 0)
+ /* Have to figure it out! */
+ if (! deduce_term_size (1, getenv ("TERM"), &output_width, 0))
+ output_width = 80; /* common default */
+ ps_fmt_set_output_width (fmt, output_width);
+ }
+
+ if (top)
+ /* Restrict output to the top TOP entries, if TOP is positive, or the
+ bottom -TOP entries, if it is negative. */
+ {
+ int filter (struct proc_stat *ps)
+ {
+ return --top >= 0;
+ }
+ if (top < 0)
+ {
+ top += procs->num_procs;
+ proc_stat_list_filter1 (procs, filter, 0, 1);
+ }
+ else
+ proc_stat_list_filter1 (procs, filter, 0, 0);
+ }
+
+ /* Finally, output all the processes! */
+ err = proc_stat_list_fmt (procs, fmt, output);
+ if (err)
+ error (5, err, "Couldn't output process status");
+}
diff --git a/utils/psout.h b/utils/psout.h
new file mode 100644
index 00000000..f4469ac4
--- /dev/null
+++ b/utils/psout.h
@@ -0,0 +1,34 @@
+/* Common output function for ps & w
+
+ Copyright (C) 1995, 1996, 1997 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 __PSOUT_H__
+#define __PSOUT_H__
+
+#include <ps.h>
+
+void psout (const struct proc_stat_list *procs,
+ const char *fmt_string, int posix_fmt,
+ const struct ps_fmt_specs *specs,
+ const char *sort_key_name, int sort_reverse,
+ int output_width, int print_heading,
+ int squash_bogus_fields, int squash_nominal_fields,
+ int top);
+
+#endif /* __PSOUT_H__ */
diff --git a/utils/rmauth.c b/utils/rmauth.c
new file mode 100644
index 00000000..4c68cd18
--- /dev/null
+++ b/utils/rmauth.c
@@ -0,0 +1,121 @@
+/* Remove authentication from selected processes
+
+ Copyright (C) 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 <string.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+#include "frobauth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (rmauth);
+
+static const struct argp_option options[] =
+{
+#ifndef UNSU
+ {"save", 's', 0, 0, "Save removed effective ids as available ids"},
+#endif
+ { 0 }
+};
+
+#ifdef UNSU
+static struct argp_child child_argps[] = {{ &frobauth_posix_argp }, { 0 }};
+#else
+static struct argp_child child_argps[] = {{ &frobauth_ea_argp }, { 0 }};
+#endif
+
+static char doc[] =
+ "Remove user/group ids from the authentication of selected processes";
+
+int
+main (int argc, char *argv[])
+{
+ int save = 0; /* save effective ids */
+ struct frobauth frobauth = FROBAUTH_INIT;
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 's': save = 1; break;
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ /* Modify UGIDS, to be what PID's new authentication should be, UGIDS is
+ what the user specified. */
+ error_t modify (struct ugids *ugids, const struct ugids *remove,
+ pid_t pid, void *hook)
+ {
+ error_t err = 0;
+ struct ugids saved = UGIDS_INIT;
+
+ if (save)
+ ugids_set (&saved, ugids);
+
+ err = ugids_subtract (ugids, remove);
+
+ if (save)
+ {
+ ugids_subtract (&saved, ugids);
+ ugids_save (&saved);
+ ugids_merge (ugids, &saved);
+ }
+
+ return err;
+ }
+ void print_info (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *removed,
+ pid_t pid, void *hook)
+ {
+ char *delta_rep;
+ struct ugids delta = UGIDS_INIT;
+
+ ugids_set (&delta, old);
+ ugids_subtract (&delta, new);
+
+ delta_rep = ugids_rep (&delta, 1, 1, 0, 0, 0);
+ printf ("%d: Removed %s\n", pid, delta_rep);
+
+ free (delta_rep);
+ ugids_fini (&delta);
+ }
+ struct argp argp = { options, parse_opt, 0, doc, child_argps };
+
+#ifdef UNSU
+ frobauth.default_user = 0;
+#endif
+ frobauth.require_ids = 1;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ if (frobauth_modify (&frobauth, 0, 0, modify, print_info, 0))
+ return 0;
+ else
+ return 1;
+}
diff --git a/utils/rpctrace.c b/utils/rpctrace.c
new file mode 100644
index 00000000..8fad04bc
--- /dev/null
+++ b/utils/rpctrace.c
@@ -0,0 +1,713 @@
+/* Trace RPCs sent to selected ports
+
+ Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Jose M. Moya <josem@gnu.org>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA. */
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+#include <mach/message.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <version.h>
+#include <sys/wait.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace);
+
+static const struct argp_option options[] = {
+ {"output", 'o', "FILE", 0, "Send trace output to FILE instead of stderr."},
+ {0}
+};
+
+static const char *args_doc = "COMMAND [ARG...]";
+static const char *doc =
+"Trace Mach Remote Procedure Calls."
+"\v.";
+
+pid_t traced_spawn (char **argv, char **envp);
+
+
+struct traced_info
+{
+ struct port_info pi;
+ union
+ {
+ struct traced_info *nextfree; /* Link when on free list. */
+
+ /* For a send right wrapper, the position in the traced_names hash table.
+ For a send-once right wrapper, this is null. */
+ void **locp;
+#define INFO_SEND_ONCE(info) ((info)->u.locp == 0)
+ } u;
+ mach_port_t forward;
+};
+
+static struct traced_info *freelist;
+
+task_t traced_task;
+ihash_t traced_names;
+struct port_class *traced_class;
+struct port_bucket *traced_bucket;
+FILE *ostream;
+
+
+/* Create a new wrapper port and do `ports_get_right' on it. */
+static mach_port_t
+new_send_wrapper (mach_port_t right)
+{
+ error_t err;
+ struct traced_info *info;
+ mach_port_t wrapper;
+
+
+ /* Use a free send-once wrapper port if we have one. */
+ if (freelist)
+ {
+ info = freelist;
+ freelist = info->u.nextfree;
+ }
+ else
+ {
+ /* Create a new wrapper port that forwards to *RIGHT. */
+ err = ports_create_port (traced_class, traced_bucket,
+ sizeof *info, &info);
+ assert_perror (err);
+ }
+
+ info->forward = right;
+
+ /* Store it in the reverse-lookup hash table, so we can
+ look up this same right again to find the wrapper port.
+ The entry in the hash table holds a weak ref on INFO. */
+ err = ihash_add (traced_names, info->forward, info, &info->u.locp);
+ assert_perror (err);
+ ports_port_ref_weak (info);
+ assert (info->u.locp != 0);
+
+ wrapper = ports_get_right (info);
+ ports_port_deref (info);
+
+ return wrapper;
+}
+
+/* Create a new wrapper port and do `ports_get_right' on it. */
+static mach_port_t
+new_send_once_wrapper (mach_port_t right)
+{
+ error_t err;
+ struct traced_info *info;
+ mach_port_t wrapper;
+
+ /* Use a free send-once wrapper port if we have one. */
+ if (freelist)
+ {
+ info = freelist;
+ freelist = info->u.nextfree;
+ }
+ else
+ {
+ /* Create a new wrapper port that forwards to *RIGHT. */
+ err = ports_create_port (traced_class, traced_bucket,
+ sizeof *info, &info);
+ assert_perror (err);
+ }
+
+ info->forward = right;
+
+ /* Send-once never compare equal to any other right (even another
+ send-once right), so there is no point in putting them in the
+ reverse-lookup table.
+
+ Since we never make send rights to this port, we don't want to
+ use the normal libports mechanisms (ports_get_right) that are
+ designed for send rights and no-senders notifications.
+ Instead, we hold on to the initial hard ref to INFO until we
+ receive a message on it. The kernel automatically sends a
+ MACH_NOTIFY_SEND_ONCE message if the send-once right dies. */
+
+ info->u.locp = 0; /* Used to mark this as send-once. */
+ wrapper = info->pi.port_right;
+
+ return wrapper;
+}
+
+
+/* This gets called when a wrapper port has no hard refs (send rights),
+ only weak refs. The only weak ref is the one held in the reverse-lookup
+ hash table. */
+static void
+traced_dropweak (void *pi)
+{
+ struct traced_info *const info = pi;
+
+ assert (info->u.locp);
+
+ /* Remove INFO from the hash table. */
+ ihash_locp_remove (traced_names, info->u.locp);
+ ports_port_deref_weak (info);
+
+ /* Deallocate the forward port, so the real port also sees no-senders. */
+ mach_port_deallocate (mach_task_self (), info->forward);
+
+ /* There are no rights to this port, so we can reuse it.
+ Add a hard ref and put INFO on the free list. */
+ ports_port_ref (info);
+ info->u.nextfree = freelist;
+ freelist = info;
+}
+
+
+
+/* Rewrite a port right in a message with an appropriate wrapper port. */
+static error_t
+rewrite_right (mach_port_t *right, mach_msg_type_name_t *type)
+{
+ error_t err;
+ struct traced_info *info;
+
+ /* We can never do anything special with a null or dead port right. */
+ if (!MACH_PORT_VALID (*right))
+ return 0;
+
+ switch (*type)
+ {
+ case MACH_MSG_TYPE_PORT_SEND:
+ /* See if we are already tracing this port. */
+ info = ihash_find (traced_names, *right);
+ if (info)
+ {
+ /* We are already tracing this port. We will pass on a right
+ to our existing wrapper port. */
+ *right = ports_get_right (info);
+ *type = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+ }
+
+ /* See if this is already one of our own wrapper ports. */
+ info = ports_lookup_port (traced_bucket, *right, 0);
+ if (info)
+ {
+ /* This is a send right to one of our own wrapper ports.
+ Instead, send along the original send right. */
+ mach_port_deallocate (mach_task_self (), *right); /* eat msg ref */
+ *right = info->forward;
+ err = mach_port_mod_refs (mach_task_self (), *right,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ ports_port_deref (info);
+ return 0;
+ }
+
+ /* We have never seen this port before. Create a new wrapper port
+ and replace the right in the message with a right to it. */
+ *right = new_send_wrapper (*right);
+ *type = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ /* There is no way to know if this send-once right is to the same
+ receive right as any other send-once or send right we have seen.
+ Fortunately, it doesn't matter, since the recipient of the
+ send-once right we pass along can't tell either. We always just
+ make a new send-once wrapper object, that will trace the one
+ message it receives, and then die. */
+ *type = MACH_MSG_TYPE_MAKE_SEND_ONCE;
+ *right = new_send_once_wrapper (*right);
+ return 0;
+
+ case MACH_MSG_TYPE_PORT_RECEIVE:
+ /* We have got a receive right, call it A. We will pass along a
+ different receive right of our own, call it B. We ourselves will
+ receive messages on A, trace them, and forward them on to B.
+
+ If A is the receive right to a send right that we have wrapped,
+ then B must be that wrapper receive right, moved from us to the
+ intended receiver of A--that way it matches previous send rights
+ to A that were sent through and replaced with our wrapper (B).
+ If not, we create a new receive right. */
+ {
+ mach_port_t rr; /* B */
+
+ info = ihash_find (traced_names, *right);
+ if (info)
+ {
+ /* This is a receive right that we have been tracing sends to. */
+ rr = ports_claim_right (info);
+ /* That released the refs on INFO, so it's been freed now. */
+ }
+ else
+ /* This is a port we know nothing about. */
+ rr = mach_reply_port ();
+
+ /* Create a new wrapper object that receives on this port. */
+ err = ports_import_port (traced_class, traced_bucket,
+ *right, sizeof *info, &info);
+ assert_perror (err);
+
+ /* Get us a send right that we will forward on. */
+ err = mach_port_insert_right (mach_task_self (), rr, rr,
+ MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ info->forward = rr;
+
+ err = ihash_add (traced_names, info->forward, info, &info->u.locp);
+ assert_perror (err);
+ ports_port_ref_weak (info);
+
+ /* If there are no extant send rights to this port, then INFO will
+ die right here and release its send right to RR.
+ XXX what to do?
+ */
+ ports_port_deref (info);
+
+ *right = rr;
+ return 0;
+ }
+
+ default:
+ assert (!"??? bogus port type from kernel!");
+ return EGRATUITOUS;
+ }
+}
+
+
+static void
+print_header (mach_msg_header_t *msg)
+{
+ fprintf (ostream,
+ "msgid %d, %d bytes in msg\n"
+ "\treply port %d (type %d)\n",
+ msg->msgh_id,
+ msg->msgh_size,
+ msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(msg->msgh_bits));
+}
+
+static void
+print_data (mach_msg_type_name_t type,
+ const void *data,
+ mach_msg_type_number_t nelt,
+ mach_msg_type_number_t eltsize)
+{
+ switch (type)
+ {
+ case MACH_MSG_TYPE_STRING:
+ fprintf (ostream, "\t\"%.*s\"\n",
+ (int) (nelt * eltsize), (const char *) data);
+ break;
+
+ case MACH_MSG_TYPE_BIT:
+ case MACH_MSG_TYPE_INTEGER_8:
+ case MACH_MSG_TYPE_INTEGER_16:
+ case MACH_MSG_TYPE_INTEGER_32:
+ case MACH_MSG_TYPE_INTEGER_64:
+ case MACH_MSG_TYPE_CHAR:
+ case MACH_MSG_TYPE_REAL:
+ default:
+ /* XXX */
+ fprintf (ostream, "\t%#x (type %d, %d*%d)\n", *(const int *)data, type,
+ nelt, eltsize);
+ break;
+ }
+}
+
+
+int
+trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ error_t err;
+ struct traced_info *info;
+ void *msg_buf_ptr = inp + 1;
+ mach_msg_bits_t complex;
+
+ /* Look up our record for the receiving port. There is no need to check
+ the class, because our port bucket only ever contains one class of
+ ports (traced_class). */
+ info = ports_lookup_port (traced_bucket, inp->msgh_local_port, 0);
+ assert (info);
+
+ if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ {
+ if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME)
+ {
+ /* If INFO is a send-once wrapper, this could be a forged
+ notification; oh well. XXX */
+
+ const mach_dead_name_notification_t *const n = (void *) inp;
+
+ assert (n->not_port == info->forward);
+ /* Deallocate extra ref allocated by the notification. */
+ mach_port_deallocate (mach_task_self (), n->not_port);
+ ports_destroy_right (info);
+ ports_port_deref (info);
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ return 1;
+ }
+ else if (inp->msgh_id == MACH_NOTIFY_NO_SENDERS
+ && !INFO_SEND_ONCE (info))
+ {
+ /* No more senders for a send right we are tracing. Now INFO
+ will die, and we will release the tracee send right so it too
+ can see a no-senders notification. */
+ mach_no_senders_notification_t *n = (void *) inp;
+ ports_no_senders (info, n->not_count);
+ ports_port_deref (info);
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ return 1;
+ }
+ }
+
+ complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX;
+
+ /* Print something about the message header. */
+ fprintf (ostream, "port %d(=>%d) receives (type %d) ",
+ inp->msgh_local_port, info->forward,
+ MACH_MSGH_BITS_LOCAL (inp->msgh_bits));
+ print_header (inp);
+
+ /* Swap the header data like a crossover cable. */
+ {
+ mach_msg_type_name_t this_type = MACH_MSGH_BITS_LOCAL (inp->msgh_bits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_REMOTE (inp->msgh_bits);
+
+ inp->msgh_local_port = inp->msgh_remote_port;
+ if (reply_type)
+ {
+ err = rewrite_right (&inp->msgh_local_port, &reply_type);
+ assert_perror (err);
+ }
+
+ inp->msgh_remote_port = info->forward;
+ if (this_type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ {
+ /* We have a message to forward for a send-once wrapper object.
+ Since each wrapper object only lives for a single message, this
+ one can be reclaimed now. We continue to hold a hard ref to the
+ ports object, but we know that nothing else refers to it now, and
+ we are consuming its `forward' right in the message we send. */
+ info->u.nextfree = freelist;
+ freelist = info;
+ }
+ else
+ this_type = MACH_MSG_TYPE_COPY_SEND;
+
+ inp->msgh_bits = complex | MACH_MSGH_BITS (this_type, reply_type);
+ }
+
+ /* Process the message data, wrapping ports and printing data. */
+ while (msg_buf_ptr < (void *) inp + inp->msgh_size)
+ {
+ mach_msg_type_t *const type = msg_buf_ptr;
+ mach_msg_type_long_t *const lt = (void *) type;
+ void *data;
+ mach_msg_type_number_t nelt; /* Number of data items. */
+ mach_msg_type_size_t eltsize; /* Bytes per item. */
+ mach_msg_type_name_t name; /* MACH_MSG_TYPE_* code */
+
+ if (!type->msgt_longform)
+ {
+ name = type->msgt_name;
+ nelt = type->msgt_number;
+ eltsize = type->msgt_size / 8;
+ data = msg_buf_ptr = type + 1;
+ }
+ else
+ {
+ name = lt->msgtl_name;
+ nelt = lt->msgtl_number;
+ eltsize = lt->msgtl_size / 8;
+ data = msg_buf_ptr = lt + 1;
+ }
+
+ if (!type->msgt_inline)
+ {
+ /* This datum is out-of-line, meaning the message actually
+ contains a pointer to a vm_allocate'd region of data. */
+ data = *(void **) data;
+ msg_buf_ptr += sizeof (void *);
+ }
+ else
+ msg_buf_ptr += ((nelt * eltsize + sizeof(natural_t) - 1)
+ & ~(sizeof(natural_t) - 1));
+
+ /* Note that MACH_MSG_TYPE_PORT_NAME does not indicate a port right.
+ It indicates a port name, i.e. just an integer--and we don't know
+ what task that port name is meaningful in. If it's meaningful in
+ a traced task, then it refers to our intercepting port rather than
+ the original port anyway. */
+ if (MACH_MSG_TYPE_PORT_ANY_RIGHT (name))
+ {
+ /* These are port rights. Translate them into wrappers. */
+ mach_port_t *const portnames = data;
+ mach_msg_type_number_t i;
+ mach_msg_type_name_t newtypes[nelt];
+ int poly;
+
+ assert (complex);
+ assert (eltsize == sizeof (mach_port_t));
+
+ fprintf (ostream, "\t%d ports, type %d\n", nelt, name);
+
+ poly = 0;
+ for (i = 0; i < nelt; ++i)
+ {
+ mach_port_t o=portnames[i];
+ newtypes[i] = name;
+
+ if (inp->msgh_id == 3215) /* mach_port_insert_right */
+ {
+ /* XXX
+ */
+ fprintf (ostream,
+ "\t\t[%d] = pass through port %d, type %d\n",
+ i, portnames[i], name);
+ continue;
+ }
+
+ err = rewrite_right (&portnames[i], &newtypes[i]);
+ assert_perror (err);
+
+ if (portnames[i] == MACH_PORT_NULL)
+ fprintf (ostream, "\t\t[%d] = null\n", i);
+ else if (portnames[i] == MACH_PORT_DEAD)
+ fprintf (ostream, "\t\t[%d] = dead name\n", i);
+ else
+ fprintf (ostream,
+ "\t\t[%d] = port %d, type %d => port %d, type %d\n",
+ i, o, name, portnames[i], newtypes[i]);
+
+ if (i > 0 && newtypes[i] != newtypes[0])
+ poly = 1;
+ }
+
+ if (poly)
+ {
+ if (name == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ {
+ /* Some of the new rights are MAKE_SEND_ONCE.
+ Turn them all into MOVE_SEND_ONCE. */
+ for (i = 0; i < nelt; ++i)
+ if (newtypes[i] == MACH_MSG_TYPE_MAKE_SEND_ONCE)
+ {
+ err = mach_port_insert_right (mach_task_self (),
+ portnames[i],
+ portnames[i],
+ newtypes[i]);
+ assert_perror (err);
+ }
+ else
+ assert (newtypes[i] == MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ }
+ else
+ {
+ for (i = 0; i < nelt; ++i)
+ switch (newtypes[i])
+ {
+ case MACH_MSG_TYPE_COPY_SEND:
+ err = mach_port_mod_refs (mach_task_self (),
+ portnames[i],
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ break;
+ case MACH_MSG_TYPE_MAKE_SEND:
+ err = mach_port_insert_right (mach_task_self (),
+ portnames[i],
+ portnames[i],
+ newtypes[i]);
+ assert_perror (err);
+ break;
+ default:
+ assert (newtypes[i] == MACH_MSG_TYPE_MOVE_SEND);
+ break;
+ }
+
+ name = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ (type->msgt_longform ? lt->msgtl_name : type->msgt_name) = name;
+ }
+ else if (newtypes[0] != name)
+ (type->msgt_longform ? lt->msgtl_name : type->msgt_name)
+ = newtypes[0];
+ }
+ else
+ print_data (name, data, nelt, eltsize);
+ }
+
+ /* Resend the message to the tracee. */
+ err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ /* The tracee port died. No doubt we are about to receive the dead-name
+ notification. */
+ /* XXX MAKE_SEND, MAKE_SEND_ONCE rights in msg not handled */
+ mach_msg_destroy (inp);
+ }
+ else
+ assert_perror (err);
+
+ ports_port_deref (info);
+
+ /* We already sent the message, so the server loop shouldn't do it again. */
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+
+ return 1;
+}
+
+
+/* This function runs in the tracing thread and drives all the tracing. */
+static any_t
+trace_thread_function (void *arg)
+{
+ struct port_bucket *const bucket = arg;
+ ports_manage_port_operations_one_thread (bucket, trace_and_forward, 0);
+ return 0;
+}
+
+
+int
+main (int argc, char **argv, char **envp)
+{
+ const char *outfile = 0;
+ char **cmd_argv = 0;
+ error_t err;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'o':
+ outfile = arg;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ case ARGP_KEY_ARG:
+ cmd_argv = &state->argv[state->next - 1];
+ state->next = state->argc;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ if (outfile)
+ {
+ ostream = fopen (outfile, "w");
+ if (!ostream)
+ error (1, errno, "%s", outfile);
+ }
+ else
+ ostream = stderr;
+
+ traced_bucket = ports_create_bucket ();
+ traced_class = ports_create_class (0, &traced_dropweak);
+
+ err = ihash_create (&traced_names);
+ assert_perror (err);
+
+ /* Spawn a single thread that will receive intercepted messages, print
+ them, and interpose on the ports they carry. The access to the
+ `traced_info' and ihash data structures is all single-threaded,
+ happening only in this new thread. */
+ cthread_detach (cthread_fork (trace_thread_function, traced_bucket));
+
+ /* Run the program on the command line and wait for it to die.
+ The other thread does all the tracing and interposing. */
+ {
+ pid_t child, pid;
+ int status;
+ child = traced_spawn (cmd_argv, envp);
+ pid = waitpid (child, &status, 0);
+ sleep (1); /* XXX gives other thread time to print */
+ if (pid != child)
+ error (1, errno, "waitpid");
+ if (WIFEXITED (status))
+ fprintf (ostream, "Child %d exited with %d\n",
+ pid, WEXITSTATUS (status));
+ else
+ fprintf (ostream, "Child %d %s\n", pid, strsignal (WTERMSIG (status)));
+ }
+
+ return 0;
+}
+
+
+/* Run a child and have it do more or else `execvpe (argv, envp);'. */
+pid_t
+traced_spawn (char **argv, char **envp)
+{
+ error_t err;
+ pid_t pid;
+ mach_port_t task_wrapper;
+ file_t file = file_name_path_lookup (argv[0], getenv ("PATH"),
+ O_EXEC, 0, 0);
+
+ if (file == MACH_PORT_NULL)
+ error (1, errno, "command not found: %s", argv[0]);
+
+ err = task_create (mach_task_self (), 0, &traced_task);
+ assert_perror (err);
+
+ /* Create a trace wrapper for the task port. */
+ task_wrapper = new_send_wrapper (traced_task);/* consumes ref */
+
+ /* Replace the task's kernel port with the wrapper. When this task calls
+ `mach_task_self ()', it will get our wrapper send right instead of its
+ own real task port. */
+ err = mach_port_insert_right (mach_task_self (), task_wrapper,
+ task_wrapper, MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ err = task_set_special_port (traced_task, TASK_KERNEL_PORT, task_wrapper);
+ assert_perror (err);
+
+ /* Declare the new task to be our child. This is what a fork does. */
+ err = proc_child (getproc (), traced_task);
+ if (err)
+ error (2, err, "proc_child");
+ pid = task2pid (traced_task);
+ if (pid < 0)
+ error (2, errno, "task2pid");
+
+ /* Now actually run the command they told us to trace. We do the exec on
+ the actual task, so the RPCs to map in the program itself do not get
+ traced. Could have an option to use TASK_WRAPPER here instead. */
+ err = _hurd_exec (traced_task, file, argv, envp);
+ if (err)
+ error (2, err, "cannot exec `%s'", argv[0]);
+
+ /* We were keeping this send right alive so that the wrapper object
+ cannot die and hence our TRACED_TASK ref cannot have been released. */
+ mach_port_deallocate (mach_task_self (), task_wrapper);
+
+ return pid;
+}
diff --git a/utils/setauth.c b/utils/setauth.c
new file mode 100644
index 00000000..73f221a0
--- /dev/null
+++ b/utils/setauth.c
@@ -0,0 +1,134 @@
+/* Change the authentication of selected processes
+
+ Copyright (C) 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 <string.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+#include "frobauth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (setauth);
+
+#define OPT_NO_SAVE 1
+
+static const struct argp_option options[] =
+{
+#ifdef SU
+ {"no-save", OPT_NO_SAVE, 0, 0, "Don't save removed effective ids as available ids"},
+#else
+ {"save", 's', 0, 0, "Save removed effective ids as available ids"},
+#endif
+ {"keep", 'k', 0, 0, "Keep old ids in addition to the new ones"},
+ { 0 }
+};
+
+static struct argp_child child_argps[] = {{ &frobauth_posix_argp }, { 0 }};
+
+static char doc[] =
+ "Change the authentication of selected processes";
+
+extern error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids);
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ auth_t auth; /* Authority to make changes. */
+ int save = 0, keep = 0;
+ struct idvec have_uids = IDVEC_INIT, have_gids = IDVEC_INIT;
+ struct frobauth frobauth = FROBAUTH_INIT;
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 's': save = 1; break;
+ case 'k': keep = 1; break;
+ case OPT_NO_SAVE: save = 0; break;
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ /* Modify UGIDS, to be what PID's new authentication should be, UGIDS is
+ what the user specified. */
+ error_t modify (struct ugids *ugids, const struct ugids *new,
+ pid_t pid, void *hook)
+ {
+ struct ugids old = UGIDS_INIT;
+ ugids_set (&old, ugids);
+
+ ugids_set (ugids, new);
+
+ if (keep)
+ ugids_merge (ugids, &old);
+ if (save)
+ {
+ ugids_save (&old);
+ ugids_merge (ugids, &old);
+ }
+
+ return 0;
+ }
+ void print_info (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *user,
+ pid_t pid, void *hook)
+ {
+ char *new_rep = ugids_rep (new, 1, 1, 0, 0, 0);
+ printf ("%d: Changed auth to %s\n", pid, new_rep);
+ free (new_rep);
+ }
+ struct argp argp = { options, parse_opt, 0, doc, child_argps };
+
+#ifdef SU
+ frobauth.default_user = 0;
+ save = 1; /* Default to saving ids */
+#endif
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ /* See what the invoking user is authorized to do. */
+ err = get_nonsugid_ids (&have_uids, &have_gids);
+ if (err)
+ error (52, err, "Cannot get invoking authentication");
+
+ /* Check passwords. */
+ err = ugids_verify_make_auth (&frobauth.ugids, &have_uids, &have_gids, 0, 0,
+ 0, 0, &auth);
+ if (err == EACCES)
+ error (15, 0, "Invalid password");
+ else if (err)
+ error (16, err, "Authentication failure");
+
+ if (frobauth_modify (&frobauth, &auth, 1, modify, print_info, 0))
+ return 0;
+ else
+ return 1;
+}
diff --git a/utils/settrans.c b/utils/settrans.c
new file mode 100644
index 00000000..408849a3
--- /dev/null
+++ b/utils/settrans.c
@@ -0,0 +1,216 @@
+/* Set a file's translator.
+
+ Copyright (C) 1995, 96, 97, 98 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 <string.h>
+#include <argp.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <error.h>
+#include <argz.h>
+#include <hurd/fshelp.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (settrans);
+
+#define DEFAULT_TIMEOUT 60
+
+#define _STRINGIFY(arg) #arg
+#define STRINGIFY(arg) _STRINGIFY (arg)
+
+static struct argp_option options[] =
+{
+ {"active", 'a', 0, 0, "Set NODE's active translator" },
+ {"passive", 'p', 0, 0, "Set NODE's passive translator" },
+ {"create", 'c', 0, 0, "Create NODE if it doesn't exist" },
+ {"dereference", 'L', 0, 0, "If a translator exists, put the new one on top"},
+ {"pause", 'P', 0, 0, "When starting an active translator, prompt and"
+ " wait for a newline on stdin before completing the startup handshake"},
+ {"timeout", 't',"SEC",0, "Timeout for translator startup, in seconds"
+ " (default " STRINGIFY (DEFAULT_TIMEOUT) "); 0 means no timeout"},
+ {"exclusive", 'x', 0, 0, "Only set the translator if there is none already"},
+
+ {0,0,0,0, "When setting the passive translator, if there's an active translator:"},
+ {"goaway", 'g', 0, 0, "Make the active translator go away"},
+ {"keep-active", 'k', 0, 0, "Leave the existing active translator running"},
+
+ {0,0,0,0, "When an active translator is told to go away:"},
+ {"recursive", 'R', 0, 0, "Shutdown its children too"},
+ {"force", 'f', 0, 0, "If it doesn't want to die, force it"},
+ {"nosync", 'S', 0, 0, "Don't sync it before killing it"},
+
+ {0, 0}
+};
+static char *args_doc = "NODE [TRANSLATOR ARG...]";
+static char *doc = "Set the passive/active translator on NODE."
+"\vBy default the passive translator is set.";
+
+/* ---------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+
+ /* The filesystem node we're putting a translator on. */
+ char *node_name = 0;
+ file_t node;
+
+ /* The translator's arg vector, in '\0' separated format. */
+ char *argz = 0;
+ int argz_len = 0;
+
+ /* The control port for any active translator we start up. */
+ fsys_t active_control = MACH_PORT_NULL;
+
+ /* Flags to pass to file_set_translator. */
+ int active_flags = 0;
+ int passive_flags = 0;
+ int lookup_flags = O_NOTRANS;
+ int goaway_flags = 0;
+
+ /* Various option flags. */
+ int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0;
+ int excl = 0;
+ int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ node_name = arg;
+ else /* command */
+ {
+ error_t err =
+ argz_create (state->argv + state->next - 1, &argz, &argz_len);
+ if (err)
+ error(3, err, "Can't create options vector");
+ state->next = state->argc; /* stop parsing */
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ case 'a': active = 1; break;
+ case 'p': passive = 1; break;
+ case 'k': keep_active = 1; break;
+ case 'g': kill_active = 1; break;
+ case 'x': excl = 1; break;
+ case 'P': pause = 1; break;
+
+ case 'c': lookup_flags |= O_CREAT; break;
+ case 'L': lookup_flags &= ~O_NOTRANS; break;
+
+ case 'R': goaway_flags |= FSYS_GOAWAY_RECURSE; break;
+ case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break;
+ case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break;
+
+ /* Use atof so the user can specifiy fractional timeouts. */
+ case 't': timeout = atof (arg) * 1000.0; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ if (!active && !passive)
+ passive = 1; /* By default, set the passive translator. */
+
+ if (passive)
+ passive_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0);
+ if (active)
+ active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0);
+
+ if (passive && !active)
+ {
+ /* When setting just the passive, decide what to do with any active. */
+ if (kill_active)
+ /* Make it go away. */
+ active_flags = FS_TRANS_SET;
+ else if (! keep_active)
+ /* Ensure that there isn't one. */
+ active_flags = FS_TRANS_SET | FS_TRANS_EXCL;
+ }
+
+ if (active && argz_len > 0)
+ {
+ /* Error during file lookup; we use this to avoid duplicating error
+ messages. */
+ error_t open_err = 0;
+
+ /* The callback to start_translator opens NODE as a side effect. */
+ error_t open_node (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type)
+ {
+ if (pause)
+ {
+ fprintf (stderr, "Pausing...");
+ getchar ();
+ }
+
+ node = file_name_lookup (node_name, flags | lookup_flags, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ open_err = errno;
+ return open_err;
+ }
+
+ *underlying = node;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+ }
+ err = fshelp_start_translator (open_node, argz, argz, argz_len, timeout,
+ &active_control);
+ if (err)
+ /* If ERR is due to a problem opening the translated node, we print
+ that name, otherwise, the name of the translator. */
+ error(4, err, "%s", (err == open_err) ? node_name : argz);
+ }
+ else
+ {
+ node = file_name_lookup(node_name, lookup_flags, 0666);
+ if (node == MACH_PORT_NULL)
+ error(1, errno, "%s", node_name);
+ }
+
+ err =
+ file_set_translator(node,
+ passive_flags, active_flags, goaway_flags,
+ argz, argz_len,
+ active_control, MACH_MSG_TYPE_COPY_SEND);
+ if (err)
+ error(5, err, "%s", node_name);
+
+ return 0;
+}
diff --git a/utils/shd.c b/utils/shd.c
new file mode 100644
index 00000000..2c08ac1f
--- /dev/null
+++ b/utils/shd.c
@@ -0,0 +1,379 @@
+/*
+ Copyright (C) 1994, 1995, 1999 Free Software Foundation
+
+ 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/types.h>
+#include <hurd.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <device/device.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <hurd/exec.h>
+
+mach_port_t proc;
+int pause_startup = 0;
+
+void
+reap (pid_t waitfor)
+{
+ pid_t pid;
+ int status;
+
+ while (1)
+ {
+ pid = waitpid (WAIT_ANY, &status, WUNTRACED | (waitfor ? 0 : WNOHANG));
+
+ if (pid == -1)
+ {
+ if (errno != ECHILD && errno != EWOULDBLOCK)
+ perror ("waitpid");
+ return;
+ }
+ else if (WIFEXITED (status))
+ printf ("PID %d exit status %d\n",
+ pid, WEXITSTATUS (status));
+ else if (WIFSIGNALED (status))
+ printf ("PID %d %s\n",
+ pid, strsignal (WTERMSIG (status)));
+ else if (WIFSTOPPED (status))
+ printf ("PID %d stopped: %s\n",
+ pid, strsignal (WSTOPSIG (status)));
+ else
+ printf ("PID %d bizarre status %#x\n", pid, status);
+
+ if (pid == waitfor)
+ waitfor = 0;
+ }
+}
+
+pid_t
+run (char **argv, int fd0, int fd1)
+{
+ file_t file;
+ char *program;
+
+ if (strchr (argv[0], '/') != NULL)
+ program = argv[0];
+ else
+ {
+ size_t len = strlen (argv[0]);
+ const char bin[] = "/bin/";
+ program = alloca (sizeof bin + len);
+ memcpy (program, bin, sizeof bin - 1);
+ memcpy (&program[sizeof bin - 1], argv[0], len + 1);
+ }
+
+ file = file_name_lookup (program, O_EXEC, 0);
+ if (file == MACH_PORT_NULL)
+ {
+ perror (program);
+ return -1;
+ }
+ else
+ {
+ task_t task;
+ pid_t pid;
+
+ errno = task_create (mach_task_self (), 0, &task);
+ if (errno)
+ {
+ perror ("task_create");
+ pid = -1;
+ }
+ else
+ {
+ int save0, save1;
+
+ inline int movefd (int from, int to, int *save)
+ {
+ if (from == to)
+ return 0;
+ *save = dup (to);
+ if (*save < 0)
+ {
+ perror ("dup");
+ return -1;
+ }
+ if (dup2 (from, to) != to)
+ {
+ perror ("dup2");
+ return -1;
+ }
+ close (from);
+ return 0;
+ }
+ inline int restorefd (int from, int to, int *save)
+ {
+ if (from == to)
+ return 0;
+ if (dup2 (*save, to) != to)
+ {
+ perror ("dup2");
+ return -1;
+ }
+ close (*save);
+ return 0;
+ }
+
+ pid = task2pid (task);
+ if (pid == -1)
+ perror ("task2pid"), pid = 0;
+ errno = proc_child (proc, task);
+ if (errno)
+ perror ("proc_child");
+ if (pause_startup)
+ {
+ printf ("Pausing (child PID %d)...", pid);
+ fflush (stdout);
+ getchar ();
+ }
+
+ if (movefd (fd0, 0, &save0) ||
+ movefd (fd1, 1, &save1))
+ return -1;
+
+ errno = _hurd_exec (task, file, argv, environ);
+
+ if (restorefd (fd0, 0, &save0) ||
+ restorefd (fd1, 1, &save1))
+ return -1;
+
+ if (errno)
+ {
+ perror ("_hurd_exec");
+ errno = task_terminate (task);
+ if (errno)
+ perror ("task_terminate");
+ }
+ mach_port_deallocate (mach_task_self (), task);
+
+ }
+ mach_port_deallocate (mach_task_self (), file);
+
+ return pid;
+ }
+}
+
+void
+command (int argc, char **argv)
+{
+ pid_t pid;
+ int bg;
+ int i, start;
+ int fds[2] = { 0, 1 };
+
+ bg = !strcmp (argv[argc - 1], "&");
+ if (bg)
+ argv[--argc] = NULL;
+
+ start = 0;
+ for (i = 1; i < argc; ++i)
+ if (! strcmp (argv[i], "|"))
+ {
+ int fd0 = fds[0];
+ argv[i] = NULL;
+ if (pipe (fds))
+ {
+ perror ("pipe");
+ return;
+ }
+ pid = run (argv + start, fd0, fds[1]);
+ if (pid < 0)
+ return;
+ start = i + 1;
+ }
+
+ pid = run (argv + start, fds[0], 1);
+
+ if (fds[0] != 0)
+ close (fds[0]);
+ if (fds[1] != 1)
+ close (fds[1]);
+
+ reap (bg ? 0 : pid);
+}
+
+
+int
+main ()
+{
+ char *linebuf = NULL;
+ size_t linebufsize = 0;
+
+ proc = getproc ();
+ assert (proc);
+
+#if 0
+ {
+ error_t err;
+ mach_port_t outp;
+ mach_port_t hostp, masterd;
+ err = proc_getprivports (proc, &hostp, &masterd);
+ assert (!err);
+
+ err = device_open (masterd, D_WRITE|D_READ, "console", &outp);
+ assert (!err);
+
+ stdin = mach_open_devstream (outp, "r");
+ stdout = stderr = mach_open_devstream (outp, "w+");
+ }
+#endif
+
+ /* Kludge to give boot a port to the auth server. */
+ exec_init (getdport (0), getauth (),
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+
+ if ((fcntl (0, F_GETFL) & O_READ) == 0)
+ {
+ int ttyd = open ("/dev/tty", O_RDWR|O_IGNORE_CTTY);
+ if (ttyd >= 0)
+ {
+ fcntl (ttyd, F_SETFD, FD_CLOEXEC);
+ stdin = fdopen (ttyd, "r");
+ stdout = stderr = fdopen (ttyd, "w");
+ }
+ }
+
+ atexit ((void (*) (void)) &sync);
+
+ while (1)
+ {
+ ssize_t n;
+
+ sync ();
+ printf ("# ");
+ fflush (stdout);
+ n = getline (&linebuf, &linebufsize, stdin);
+ if (n == -1)
+ {
+ if (feof (stdin))
+ return 0;
+ perror ("getline");
+ continue;
+ }
+
+ if (linebuf[n - 1] == '\n')
+ linebuf[--n] = '\0';
+
+ if (n > 0)
+ {
+ char *argv[(n + 1) / 2 + 1];
+ int argc;
+ char *line, *p;
+
+ line = linebuf;
+ argc = 0;
+ while ((p = strsep (&line, " \t\n\f\v")) != NULL)
+ argv[argc++] = p;
+ argv[argc] = NULL;
+
+ if (!strcmp (argv[0], "exit"))
+ {
+ reap (0);
+ exit (0);
+ }
+ else if (!strcmp (argv[0], "pause"))
+ pause_startup = 1;
+ else if (!strcmp (argv[0], "nopause"))
+ pause_startup = 0;
+ else if (!strcmp (argv[0], "kill"))
+ {
+ if (argc == 1)
+ fprintf (stderr, "Usage: kill PID ...\n");
+ else
+ {
+ int pid;
+ task_t task;
+ int i;
+ for (i = 1; i < argc; i++)
+ {
+ pid = atoi (argv[i]);
+ printf ("Killing pid %d\n", pid);
+ if (pid)
+ {
+ proc_pid2task (proc, pid, &task);
+ task_terminate (task);
+ mach_port_deallocate (mach_task_self (), task);
+ }
+ }
+ }
+ }
+ else if (!strcmp (argv[0], "cd"))
+ {
+ if (argc != 2)
+ fprintf (stderr, "Usage: cd DIRECTORY\n");
+ else if (chdir (argv[1]))
+ perror ("chdir");
+ }
+ else if (!strcmp (argv[0], "exec"))
+ {
+ if (argc == 1)
+ fprintf (stderr, "Usage: exec PROGRAM [ARGS...]\n");
+ else
+ {
+ char *program;
+ if (strchr (argv[1], '/') != NULL)
+ program = argv[1];
+ else
+ {
+ size_t len = strlen (argv[1]);
+ const char bin[] = "/bin/";
+ program = alloca (sizeof bin + len);
+ memcpy (program, bin, sizeof bin - 1);
+ memcpy (&program[sizeof bin - 1], argv[1], len + 1);
+ }
+ if (execv (program, &argv[1]) == 0)
+ fprintf (stderr, "execv (%s) returned 0!\n", program);
+ else
+ perror ("execv");
+ }
+ }
+ else if (!strcmp (argv[0], "setenv"))
+ {
+ if (argc != 3)
+ fprintf (stderr, "Usage: setenv VAR VALUE\n");
+ else if (setenv (argv[1], argv[2], 1))
+ perror ("setenv");
+ }
+ else if (!strcmp (argv[0], "fork"))
+ {
+ pid_t pid = fork ();
+ switch (pid)
+ {
+ case -1:
+ perror ("fork");
+ break;
+ case 0:
+ printf ("I am the child, PID %d.\n", (int) getpid ());
+ break;
+ default:
+ printf ("I am the parent of child with PID %d.\n", pid);
+ reap (argc == 2 && !strcmp (argv[1], "&") ? 0 : pid);
+ break;
+ }
+ }
+ else
+ command (argc, argv);
+ }
+ reap (0);
+ fflush (stderr);
+ }
+}
diff --git a/utils/showtrans.c b/utils/showtrans.c
new file mode 100644
index 00000000..dbbada75
--- /dev/null
+++ b/utils/showtrans.c
@@ -0,0 +1,145 @@
+/* Show files' passive translators.
+
+ 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 <string.h>
+#include <argp.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <version.h>
+#include <sys/mman.h>
+
+#include <error.h>
+#include <argz.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (showtrans);
+
+static struct argp_option options[] =
+{
+ {"prefix", 'p', 0, 0, "Always display `FILENAME: ' before translators"},
+ {"no-prefix", 'P', 0, 0, "Never display `FILENAME: ' before translators"},
+ {"silent", 's', 0, 0, "No output; useful when checking error status"},
+ {"quiet", 'q', 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"translated",'t', 0, 0, "Only display files that have translators"},
+ {0, 0}
+};
+
+static char *args_doc = "FILE...";
+static char *doc = "Show the passive translator of FILE..."
+"\vA FILE argument of `-' prints the translator on the node"
+" attached to standard input.";
+
+/* ---------------------------------------------------------------- */
+
+int
+main (int argc, char *argv[])
+{
+ /* The default exit status -- changed to 0 if we find any translators. */
+ int status = 1;
+ /* Some option flags. -1 for PRINT_PREFIX means use the default. */
+ int print_prefix = -1, silent = 0, show_untrans = 1;
+
+ /* If NODE is MACH_PORT_NULL, prints an error message and exits, otherwise
+ prints the translator on NODE, possibly prefixed by `NAME:', and
+ deallocates NODE. */
+ void print_node_trans (file_t node, char *name)
+ {
+ if (node == MACH_PORT_NULL)
+ error (0, errno, "%s", name);
+ else
+ {
+ char buf[1024], *trans = buf;
+ int trans_len = sizeof (buf);
+ error_t err = file_get_translator (node, &trans, &trans_len);
+
+ switch (err)
+ {
+ case 0:
+ /* Make the '\0's in TRANS printable. */
+ argz_stringify (trans, trans_len, ' ');
+
+ if (!silent)
+ {
+ if (print_prefix)
+ printf ("%s: %s\n", name, trans);
+ else
+ puts (trans);
+ }
+
+ if (trans != buf)
+ munmap (trans, trans_len);
+
+ status = 0;
+
+ break;
+
+ case EINVAL:
+ /* NODE just doesn't have a translator. */
+ if (!silent && print_prefix && show_untrans)
+ puts (name);
+ break;
+
+ default:
+ error (0, err, "%s", name);
+ }
+
+ mach_port_deallocate (mach_task_self (), node);
+ }
+ }
+
+ /* Parse a command line option. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG: /* A FILE argument */
+ if (print_prefix < 0)
+ /* By default, only print a prefix if there are multiple files. */
+ print_prefix = state->next < state->argc;
+
+ if (strcmp (arg, "-") != 0)
+ print_node_trans (file_name_lookup (arg, O_NOTRANS, 0), arg);
+ else
+ print_node_trans (getdport (0), "-");
+ break;
+
+ /* Options. */
+ case 'p': print_prefix = 1; break;
+ case 'P': print_prefix = 0; break;
+ case 's': case 'q': silent = 1; break;
+ case 't': show_untrans = 0; break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state); /* exits */
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ return status;
+}
diff --git a/utils/storecat.c b/utils/storecat.c
new file mode 100644
index 00000000..be65b63a
--- /dev/null
+++ b/utils/storecat.c
@@ -0,0 +1,69 @@
+/* Write a store to stdout
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <argp.h>
+#include <error.h>
+
+#include <hurd/store.h>
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct store *s;
+ char *name;
+ off_t addr;
+ size_t left;
+ const struct argp_child kids[] = { { &store_argp }, { 0 }};
+ struct argp argp =
+ { 0, 0, 0, "Write the contents of a store to stdout", kids };
+ struct store_argp_params p = { 0 };
+
+ argp_parse (&argp, argc, argv, 0, 0, &p);
+ err = store_parsed_name (p.result, &name);
+ if (err)
+ error (2, err, "store_parsed_name");
+
+ err = store_parsed_open (p.result, STORE_READONLY, &s);
+ if (err)
+ error (4, err, "%s", name);
+
+ addr = 0;
+ left = s->size;
+ while (left > 0)
+ {
+ size_t read = left > 1024*1024 ? 1024*1024 : left;
+ char buf[4096];
+ void *data = buf;
+ size_t data_len = sizeof (buf);
+
+ err = store_read (s, addr, read, &data, &data_len);
+ if (err)
+ error (5, err, "%s", name);
+ if (write (1, data, data_len) < 0)
+ error (6, errno, "stdout");
+
+ addr += data_len >> s->log2_block_size;
+ left -= data_len;
+ }
+
+ exit (0);
+}
diff --git a/utils/storeinfo.c b/utils/storeinfo.c
new file mode 100644
index 00000000..638cebc7
--- /dev/null
+++ b/utils/storeinfo.c
@@ -0,0 +1,255 @@
+/* Show where a file exists
+
+ Copyright (C) 1995,96,97,98,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 <string.h>
+#include <argp.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <version.h>
+
+#include <error.h>
+
+#include <hurd/fs.h>
+#include <hurd/store.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (storeinfo);
+
+static struct argp_option options[] =
+{
+ {"type", 't', 0, 0, "Print the type of store behind FILE"},
+ {"flags", 'f', 0, 0, "Print the flags associated with FILE's store"},
+ {"name", 'n', 0, 0, "Print the name of the store behind FILE"},
+ {"blocks", 'b', 0, 0, "Print the number of blocks in FILE"},
+ {"block-size", 'B', 0, 0, "Print the block size of FILE's store"},
+ {"size", 's', 0, 0, "Print the size, in bytes, of FILE"},
+ {"block-list", 'l', 0, 0, "Print the blocks that are in FILE"},
+ {"children", 'c', 0, 0, "If the store has children, show them too"},
+ {"dereference", 'L', 0, 0, "If FILE is a symbolic link, follow it"},
+ {"prefix", 'p', 0, 0, "Always print `FILE: ' before info"},
+ {"no-prefix", 'P', 0, 0, "Never print `FILE: ' before info"},
+ {0, 0}
+};
+static char *args_doc = "FILE...";
+static char *doc = "Show information about storage used by FILE..."
+"\vWith no FILE arguments, the file attached to standard"
+" input is used. The fields to be printed are separated by colons, in this"
+" order: PREFIX: TYPE (FLAGS): NAME: BLOCK-SIZE: BLOCKS: SIZE: BLOCK-LIST."
+" If the store is a composite one and --children is specified, children"
+" are printed on lines following the main store, indented accordingly."
+" By default, all fields, and children, are printed.";
+
+/* ---------------------------------------------------------------- */
+
+/* Things we can print about a file's storage. */
+#define W_SOURCE 0x01
+#define W_TYPE 0x02
+#define W_NAME 0x04
+#define W_BLOCKS 0x08
+#define W_BLOCK_SIZE 0x10
+#define W_SIZE 0x20
+#define W_RUNS 0x40
+#define W_CHILDREN 0x80
+#define W_FLAGS 0x100
+
+#define W_ALL 0x1FF
+
+/* Print a line of information (exactly what is determinted by WHAT)
+ about store to stdout. LEVEL is the desired indentation level. */
+static void
+print_store (struct store *store, int level, unsigned what)
+{
+ int i;
+ int first = 1;
+
+ void psep ()
+ {
+ if (first)
+ first = 0;
+ else
+ {
+ putchar (':');
+ putchar (' ');
+ }
+ }
+ void pstr (const char *str, unsigned mask)
+ {
+ if ((what & mask) == mask)
+ {
+ psep ();
+ fputs (str ?: "-", stdout);
+ }
+ }
+ void pint (off_t val, unsigned mask)
+ {
+ if ((what & mask) == mask)
+ {
+ psep ();
+ printf ("%ld", val);
+ }
+ }
+
+ /* Indent */
+ for (i = 0; i < level; i++)
+ {
+ putchar (' ');
+ putchar (' ');
+ }
+
+ pstr (store->class->name,W_TYPE);
+
+ if ((store->flags & ~STORE_INACTIVE) && (what & W_FLAGS))
+ {
+ int t = 0; /* flags tested */
+ int f = 1;
+ void pf (int mask, char *name)
+ {
+ if (store->flags & mask)
+ {
+ if (f)
+ f = 0;
+ else
+ putchar (',');
+ fputs (name, stdout);
+ }
+ t |= mask;
+ }
+
+ if (! first)
+ putchar (' ');
+ first = 0;
+ putchar ('(');
+
+ pf (STORE_READONLY, "ro");
+ pf (STORE_HARD_READONLY, "h_ro");
+ pf (STORE_ENFORCED, "enf");
+ pf (STORAGE_MUTATED, "mut");
+
+ if (store->flags & ~(t | STORE_INACTIVE))
+ /* Leftover flags. */
+ {
+ if (! f)
+ putchar (';');
+ printf ("0x%x", store->flags & ~(t | STORE_INACTIVE));
+ }
+ putchar (')');
+ }
+
+ pstr (store->name, W_NAME);
+ pint (store->block_size, W_BLOCK_SIZE);
+ pint (store->blocks, W_BLOCKS);
+ pint (store->size, W_SIZE);
+
+ if (what & W_RUNS)
+ {
+ psep ();
+ for (i = 0; i < store->num_runs; i++)
+ {
+ if (i > 0)
+ putchar (',');
+ if (store->runs[i].start < 0)
+ /* A hole */
+ printf ("@+%ld", store->runs[i].length);
+ else
+ printf ("%ld+%ld", store->runs[i].start, store->runs[i].length);
+ }
+ }
+
+ putchar ('\n');
+
+ if (what & W_CHILDREN)
+ /* Show info about stores that make up this one. */
+ for (i = 0; i < store->num_children; i++)
+ print_store (store->children[i], level + 1, what);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int deref = 0, print_prefix = -1;
+ unsigned what = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ void info (mach_port_t file, char *source, error_t err)
+ {
+ struct store *store;
+
+ if (file == MACH_PORT_NULL)
+ error (3, err, source);
+
+ if (print_prefix < 0)
+ /* By default, only print filename prefixes for multiple files. */
+ print_prefix = state->next < state->argc;
+
+ if (what == 0)
+ what = W_ALL;
+
+ err = store_create (file, STORE_INACTIVE, 0, &store);
+ if (err)
+ error (4, err, source);
+
+ print_store (store, 0, what);
+ store_free (store);
+ }
+
+ switch (key)
+ {
+ case 'L': deref = 1; break;
+ case 'p': print_prefix = 1; break;
+ case 'P': print_prefix = 0; break;
+
+ case 't': what |= W_TYPE; break;
+ case 'f': what |= W_FLAGS; break;
+ case 'n': what |= W_NAME; break;
+ case 'b': what |= W_BLOCKS; break;
+ case 'B': what |= W_BLOCK_SIZE; break;
+ case 's': what |= W_SIZE; break;
+ case 'l': what |= W_RUNS; break;
+ case 'c': what |= W_CHILDREN; break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+
+ case ARGP_KEY_ARG:
+ if (strcmp (arg, "-") == 0)
+ info (getdport (0), "-", 0);
+ else
+ {
+ file_t file = file_name_lookup (arg, deref ? 0 : O_NOLINK, 0);
+ info (file, arg, errno);
+ mach_port_deallocate (mach_task_self (), file);
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ return 0;
+}
diff --git a/utils/storeread.c b/utils/storeread.c
new file mode 100644
index 00000000..80c36466
--- /dev/null
+++ b/utils/storeread.c
@@ -0,0 +1,120 @@
+/* Write portions of a store to stdout
+
+ Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <argp.h>
+#include <error.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+
+#include <hurd/store.h>
+
+struct argp_option options[] = {
+ {"file", 'f', 0, 0, "Use file IO instead of the raw device"},
+ {"block-size", 'b', "BYTES", 0, "Set the file block size"},
+ {0, 0}
+};
+char *arg_doc = "FILE [ADDR [LENGTH]]...";
+char *doc = "\vADDR is in blocks, and defaults to 0; LENGTH is in bytes, and defaults to the remainder of FILE.";
+
+int
+main (int argc, char **argv)
+{
+ struct store *store = 0;
+ off_t addr = -1;
+ int dumped = 0, use_file_io = 0, block_size = 0;
+
+ void dump (off_t addr, ssize_t len)
+ {
+ char buf[4096];
+ void *data = buf;
+ size_t data_len = sizeof (buf);
+ error_t err =
+ store_read (store, addr, len < 0 ? store->size : len,
+ &data, &data_len);
+ if (err)
+ error (5, err, store->name ? "%s" : "<store>", store->name);
+ if (write (1, data, data_len) < 0)
+ error (6, errno, "stdout");
+ if (data != buf)
+ munmap (data, data_len);
+ }
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'f': use_file_io = 1; break;
+ case 'b': block_size = atoi (arg); break;
+
+ case ARGP_KEY_ARG:
+ if (! store)
+ {
+ error_t err;
+ file_t source = file_name_lookup (arg, O_READ, 0);
+ if (errno)
+ error (2, errno, "%s", arg);
+ if (use_file_io)
+ if (block_size)
+ {
+ struct stat stat;
+ err = io_stat (source, &stat);
+ if (! err)
+ {
+ struct store_run run = {0, stat.st_size / block_size};
+ err = _store_file_create (source, 0, block_size, &run, 1,
+ &store);
+ }
+ }
+ else
+ err = store_file_create (source, 0, &store);
+ else
+ err = store_create (source, 0, 0, &store);
+ if (err)
+ error (err, 3, "%s", arg);
+ }
+ else if (addr < 0)
+ addr = atoi (arg);
+ else
+ {
+ dump (addr, atoi (arg));
+ dumped = 1;
+ addr = -1;
+ }
+ break;
+
+ case ARGP_KEY_END:
+ if (addr >= 0)
+ dump (addr, -1);
+ else if (! dumped)
+ dump (0, -1);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, arg_doc, doc};
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+ exit (0);
+}
diff --git a/utils/su.c b/utils/su.c
new file mode 100644
index 00000000..53563fa3
--- /dev/null
+++ b/utils/su.c
@@ -0,0 +1,2 @@
+#define SU
+#include "setauth.c"
diff --git a/utils/sush.sh b/utils/sush.sh
new file mode 100644
index 00000000..4fccd75e
--- /dev/null
+++ b/utils/sush.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+# A unix-like su (one which invokes a sub-shell).
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+#
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+#
+# 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.
+#
+
+ARGS_DOC="[USER|- [COMMAND [ARG...]]]"
+USAGE="Usage: $0 $ARGS_DOC"
+DOC="Start a new shell, or COMMAND, as USER"
+
+LOGIN=${LOGIN-/bin/login}
+FMT=${FMT-/bin/fmt}
+
+needs_arg=""
+for I; do
+ case $needs_arg in
+ ?*) needs_arg="";;
+ "")
+ case "$I" in
+ -e|-E|-g|-G|-u|-U|--envar|--envva|--env|--en|--e|--envvar-default|--envvar-defaul|--envvar-defau|--envvar-defa|--envvar-def|--envvar-def|--envvar-de|--envvar-d|--envvar-|--group|--grou|--gro|--gr|--g|--avail-group|--avail-grou|--avail-gro|--avail-gr|--avail-g|--user|--use|--us|--u|--avail-user|--avail-use|--avail-us|--avail-u)
+ needs_arg="$I";;
+ -e*|-E*|-g*|-G*|-u*|-U*|--envar=*|--envva=*|--env=*|--en=*|--e=*|--envvar-default=*|--envvar-defaul=*|--envvar-defau=*|--envvar-defa=*|--envvar-def=*|--envvar-def=*|--envvar-de=*|--envvar-d=*|--envvar-=*|--group=*|--grou=*|--gro=*|--gr=*|--g=*|--avail-group=*|--avail-grou=*|--avail-gro=*|--avail-gr=*|--avail-g=*|--user=*|--use=*|--us=*|--u=*|--avail-user=*|--avail-use=*|--avail-us=*|--avail-u=*)
+ :;;
+ --avail-|--avail|--avai|--ava|--av|--a|--avail-=*|--avail=*|--avai=*|--ava=*|--av=*|--a=*)
+ echo 1>&2 "$0: option \`$1' is ambiguous"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ --help|"-?" |--hel|--he|--h)
+ echo "$USAGE"
+ echo "$DOC"
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " -e ENTRY, --envvar=ENTRY Add ENTRY to the environment"
+ echo " -E ENTRY, --envvar-default=ENTRY"
+ echo " Use ENTRY as a default environment variable"
+ echo " -g GROUP, --group=GROUP Add GROUP to the effective groups"
+ echo " -G GROUP, --avail-group=GROUP Add GROUP to the available groups"
+ echo " -u USER, --user=USER Add USER to the effective uids"
+ echo " -U USER, --avail-user=USER Add USER to the available uids"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ echo ""
+ echo "Unlike the traditional unix \`su' command, if USER is not specified,"
+ echo "then the result is *no* user-ids, not uid 0."
+ exit 0;;
+ --usage |--usag|--usa|--us|--u)
+ (echo "Usage: $0 [-V?]"
+ echo " [-e ENTRY] [-E ENTRY] [-g GROUP] [-G GROUP] [-u USER] [-U USER] [--envvar=ENTRY] [--envvar-default=ENTRY] [--group=GROUP] [--avail-group=GROUP][--group=GROUP] [--avail-group=GROUP] [--user=USER] [--avail-user=USER] [--help] [--usage] [--version] $ARGS_DOC") |$FMT -t
+ exit 0;;
+ --version|-V |--versio|--versi|--vers|--ver|--ve|--v)
+ echo "STANDARD_HURD_VERSION_sush_"; exit 0;;
+ -)
+ : ;;
+ --)
+ break;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac;;
+ esac
+done
+
+case "$needs_arg" in ?*)
+ echo 1>&2 "$0: option \`$1' requires an argument"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+esac
+
+exec $LOGIN --program-name="$0" -pxSLf -aHOME -aMOTD -aUMASK -aBACKUP_SHELLS "$@"
diff --git a/utils/syncfs.c b/utils/syncfs.c
new file mode 100644
index 00000000..3434f5c6
--- /dev/null
+++ b/utils/syncfs.c
@@ -0,0 +1,80 @@
+/* syncfs -- User interface to file_syncfs, synchronize filesystems.
+ Copyright (C) 1994, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
+
+ 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. */
+
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (sync);
+
+static int synchronous = 0, do_children = 1;
+
+static void
+sync_one (const char *name, file_t port)
+{
+ error_t err = (port == MACH_PORT_NULL ? errno
+ : file_syncfs (port, synchronous, do_children));
+ if (err)
+ error (1, err, "%s", name);
+}
+
+static error_t
+parser (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 's': synchronous = 1; break;
+ case 'c': do_children = 0; break;
+
+ case ARGP_KEY_NO_ARGS:
+ sync_one ("/", getcrdir ());
+ break;
+
+ case ARGP_KEY_ARG:
+ sync_one (arg, file_name_lookup (arg, 0, 0));
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ static struct argp_option options[] =
+ {
+ {"synchronous", 's', 0, 0, "Wait for completion of all disk writes"},
+ {"no-children", 'c', 0, 0, "Do not synchronize child filesystems"},
+ {0}
+ };
+ struct argp argp =
+ {options, parser,
+ "[FILE...]", "Force all pending disk writes to be done immediately"
+ "\vThe filesystem containing each FILE is synchronized, and its child"
+ " filesystems unless --no-children is specified. With no FILE argument"
+ " synchronizes the root filesystem."};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ return 0;
+}
diff --git a/utils/unsu.c b/utils/unsu.c
new file mode 100644
index 00000000..bea4c98a
--- /dev/null
+++ b/utils/unsu.c
@@ -0,0 +1,90 @@
+/* Attempt to undo a previous su
+
+ Copyright (C) 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 <string.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+#include "frobauth.h"
+#include "pids.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (unsu);
+
+static struct argp_child child_argps[] = {{ &frobauth_no_ugids_argp }, { 0 }};
+
+static char doc[] =
+ "Attempt to undo a previous su"
+ "\vThis command is convenient, but will only correctly undo a limited"
+ " subset of possible su commands. It works by simply deleting all"
+ " current effective ids and the first two available ids, and then"
+ " making the first remaining available id the current effective id.";
+
+int
+main (int argc, char *argv[])
+{
+ struct frobauth frobauth = FROBAUTH_INIT;
+
+ /* Modify UGIDS, to be what PID's new authentication should be, NOISE is
+ ignored. */
+ error_t modify (struct ugids *ugids, const struct ugids *noise,
+ pid_t pid, void *hook)
+ {
+ error_t err = 0;
+
+ idvec_clear (&ugids->eff_uids);
+ idvec_clear (&ugids->eff_gids);
+ idvec_clear (&ugids->imp_eff_gids);
+
+ idvec_delete (&ugids->avail_uids, 0);
+ idvec_delete (&ugids->avail_uids, 0);
+
+ idvec_delete (&ugids->avail_gids, 0);
+ idvec_delete (&ugids->avail_gids, 0);
+ idvec_keep (&ugids->imp_avail_gids, &ugids->avail_gids);
+
+ if (ugids->avail_uids.num > 0)
+ err = ugids_set_posix_user (ugids, ugids->avail_uids.ids[0]);
+
+ return err;
+ }
+ void print_info (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *removed,
+ pid_t pid, void *hook)
+ {
+ char *new_rep = ugids_rep (new, 1, 1, 0, 0, 0);
+ printf ("%d: Changed auth to %s\n", pid, new_rep);
+ free (new_rep);
+ }
+ struct argp argp = { 0, 0, 0, doc, child_argps };
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ if (frobauth_modify (&frobauth, 0, 0, modify, print_info, 0))
+ return 0;
+ else
+ return 1;
+}
diff --git a/utils/uptime.sh b/utils/uptime.sh
new file mode 100644
index 00000000..8e52c81b
--- /dev/null
+++ b/utils/uptime.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Show system uptime, number of users, and load
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+#
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+#
+# 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.
+#
+
+USAGE="Usage: $0 [OPTION...]"
+DOC="Show system uptime, number of users, and load"
+
+W=${W-/bin/w}
+
+while :; do
+ case "$1" in
+ --help|"-?")
+ echo "$USAGE"
+ echo "$DOC"
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ exit 0;;
+ --usage)
+ echo "Usage: $0 [-V?] [--help] [--usage] [--version]"
+ exit 0;;
+ --version|-V)
+ echo "STANDARD_HURD_VERSION_uptime_"; exit 0;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac
+done
+
+case "$#" in
+ 0) :;;
+ *)
+ echo 1>&2 "$0: too many arguments"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+esac
+
+$W -u
diff --git a/utils/vminfo.c b/utils/vminfo.c
new file mode 100644
index 00000000..4440c00b
--- /dev/null
+++ b/utils/vminfo.c
@@ -0,0 +1,242 @@
+/* Print task vm information
+
+ Copyright (C) 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 <stddef.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <version.h>
+
+#include <mach.h>
+#include <mach/vm_statistics.h>
+#include <mach/default_pager.h>
+#include <hurd.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (vminfo);
+
+static const struct argp_option options[] = {
+ {"verbose", 'v', 0, 0, "Give more detailed information"},
+ {"addresses", 'a', 0, 0, "Print region start addresses"},
+ {"sizes", 's', 0, 0, "Print region sizes"},
+ {"decimal", 'd', 0, 0, "Show number is decimal"},
+ {"holes", 'h', 0, 0, "Show holes between regions explicitly"},
+ {0}
+};
+static const char *args_doc = "PID [ADDR [SIZE]]]";
+static const char *doc = "Show virtual memory regions for process PID"
+"\vIf ADDR, and possibly SIZE, are given only regions enclosing the range"
+" ADDR to ADDR+SIZE are shown (SIZE defaults to 0)."
+"\nIf neither --addresses nor --sizes is specified, both are assumed.";
+
+/* Possible things to show about regions. */
+#define W_ADDRS 0x1
+#define W_SIZES 0x2
+#define W_DETAILS 0x4
+
+static char *
+prot_rep (vm_prot_t prot)
+{
+ if (prot == 0)
+ return "0";
+ else
+ {
+ static char buf[20];
+ char *p = buf;
+ if (prot & VM_PROT_READ)
+ *p++ = 'R';
+ if (prot & VM_PROT_WRITE)
+ *p++ = 'W';
+ if (prot & VM_PROT_EXECUTE)
+ *p++ = 'X';
+ if (prot & ~VM_PROT_ALL)
+ sprintf (p, "+%#x", (prot & ~VM_PROT_ALL));
+ else
+ *p = '\0';
+ return buf;
+ }
+}
+
+static char *
+inh_rep (vm_inherit_t inh)
+{
+ static char buf[20];
+ switch (inh)
+ {
+ case VM_INHERIT_SHARE: return "share";
+ case VM_INHERIT_COPY: return "copy";
+ case VM_INHERIT_NONE: return "none";
+ default:
+ sprintf (buf, "%d", inh);
+ return buf;
+ }
+}
+
+static unsigned
+parse_num (char *arg, unsigned base, struct argp_state *state, char *what)
+{
+ char *arg_end;
+ unsigned long num = strtoul (arg, &arg_end, base);
+ if (*arg == '\0' || *arg_end != '\0')
+ argp_error (state, "%s: Invalid %s", arg, what);
+ return num;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ int what = 0, hex = 1, holes = 0;
+ vm_offset_t addr = 0, max_addr = ~addr;
+ task_t task;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ pid_t pid;
+ process_t proc;
+
+ case 'a': what |= W_ADDRS; break;
+ case 's': what |= W_SIZES; break;
+ case 'v': what |= W_DETAILS; break;
+ case 'd': hex = 0; break;
+ case 'h': holes = 1; break;
+
+ case ARGP_KEY_ARG:
+ switch (state->arg_num)
+ {
+ case 0: /* PID */
+ pid = parse_num (arg, 10, state, "PID");
+ proc = getproc ();
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ argp_failure (state, 11, err, "%s", arg);
+ break;
+ case 1: /* ADDR */
+ addr = max_addr = parse_num (arg, 0, state, "address"); break;
+ case 2: /* SIZE */
+ max_addr = addr + parse_num (arg, 0, state, "size"); break;
+ default:
+ argp_usage (state);
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if ((what & ~W_DETAILS) == 0)
+ what = W_ADDRS | W_SIZES | W_DETAILS;
+
+ while (addr <= max_addr)
+ {
+ vm_size_t size;
+ vm_prot_t prot, max_prot;
+ mach_port_t obj;
+ vm_offset_t offs;
+ vm_inherit_t inh;
+ int shared;
+ vm_offset_t hole_addr = addr;
+
+ err =
+ vm_region (task, &addr, &size, &prot, &max_prot, &inh, &shared,
+ &obj, &offs);
+ if (err)
+ {
+ if (err != EKERN_NO_SPACE)
+ error (12, err, "vm_region");
+ break;
+ }
+
+ if (holes && hole_addr != addr)
+ {
+ if ((what & (W_ADDRS|W_SIZES)) == (W_ADDRS|W_SIZES))
+ {
+ if (hex)
+ printf (" [%#x] (hole)\n", addr - hole_addr);
+ else
+ printf (" [%d] (hole)\n", addr - hole_addr);
+ }
+ else if ((what & (W_ADDRS|W_SIZES)) == W_SIZES)
+ {
+ if (hex)
+ printf ("%#10x (hole)\n", addr - hole_addr);
+ else
+ printf ("%10u (hole)\n", addr - hole_addr);
+ }
+ }
+
+ if ((what & (W_ADDRS|W_SIZES)) == (W_ADDRS|W_SIZES))
+ if (hex)
+ printf ("%#10x[%#x]", addr, size);
+ else
+ printf ("%10u[%d]", addr, size);
+ else if ((what & (W_ADDRS|W_SIZES)) == W_ADDRS)
+ if (hex)
+ printf ("%#10x", addr);
+ else
+ printf ("%10u", addr);
+ else if ((what & (W_ADDRS|W_SIZES)) == W_SIZES)
+ {
+ if (hex)
+ printf ("%#10x", size);
+ else
+ printf ("%10u", size);
+ }
+ if (what & W_DETAILS)
+ {
+ printf (" (prot=%s", prot_rep (prot));
+ if (max_prot != prot)
+ printf (", max_prot=%s", prot_rep (max_prot));
+ if (inh != VM_INHERIT_DEFAULT)
+ printf (", inherit=%s", inh_rep (inh));
+ if (shared)
+ printf (", shared");
+ if (obj != MACH_PORT_NULL)
+ printf (", mem_obj=%d", obj);
+ if (offs != 0)
+ {
+ if (hex)
+ printf (", offs=%#x", offs);
+ else
+ printf (", offs=%d", offs);
+ }
+ putchar (')');
+ }
+ putchar ('\n');
+
+ addr += size;
+ }
+
+ exit (0);
+}
diff --git a/utils/vmstat.c b/utils/vmstat.c
new file mode 100644
index 00000000..08449e07
--- /dev/null
+++ b/utils/vmstat.c
@@ -0,0 +1,632 @@
+/* Print vm statistics
+
+ Copyright (C) 1996, 1997, 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 <stdio.h>
+#include <stddef.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <version.h>
+
+#include <mach.h>
+#include <mach/vm_statistics.h>
+#include <mach/default_pager.h>
+#include <hurd.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (vmstat);
+
+static const struct argp_option options[] = {
+ {"terse", 't', 0, 0, "Use short one-line output format"},
+ {"no-header", 'H', 0, 0, "Don't print a descriptive header line"},
+ {"prefix", 'p', 0, 0, "Always display a description before stats"},
+ {"no-prefix", 'P', 0, 0, "Never display a description before stats"},
+ {"pages", 'v', 0, 0, "Display sizes in pages"},
+ {"kilobytes", 'k', 0, 0, "Display sizes in 1024 byte blocks"},
+ {"bytes", 'b', 0, 0, "Display sizes in bytes"},
+ {0}
+};
+static const char *args_doc = "[PERIOD [COUNT [HEADER_INTERVAL]]]";
+static const char *doc = "Show system virtual memory statistics"
+"\vIf PERIOD is supplied, then terse mode is"
+" selected, and the output repeated every PERIOD seconds, with cumulative"
+" fields given the difference from the last output. If COUNT is given"
+" and non-zero, only that many lines are output. HEADER_INTERVAL"
+" defaults to 23, and if not zero, is the number of repeats after which a"
+" blank line and the header will be reprinted (as well as the totals for"
+" cumulative fields).";
+
+/* We use this one type to represent all values printed by this program. It
+ should be signed, and hopefully large enough (it may need to be larger
+ than what the system returns values in, as we represent some quantities as
+ bytes instead of pages)! */
+typedef long long val_t;
+#define BADVAL ((val_t) -1LL) /* a good generic value for "couldn't get" */
+
+/* What a given number describes. */
+enum val_type
+{
+ COUNT, /* As-is. */
+ SIZE, /* Use the most convenient unit, with suffix */
+ PAGESZ, /* Like SIZE, but never changed to PAGES. */
+ PCENT, /* Append `%'. */
+};
+
+/* Return the `nominal' width of a field of type TYPE, in units of SIZE_UNITS. */
+static size_t
+val_width (val_t val, enum val_type type, size_t size_units)
+{
+ size_t vwidth (val_t val)
+ {
+ size_t w = 1;
+ if (val < 0)
+ w++, val = -val;
+ while (val > 9)
+ w++, val /= 10;
+ return w;
+ }
+ if (type == PCENT)
+ return vwidth (val) + 1;
+ else if ((type == SIZE || type == PAGESZ) && size_units == 0)
+ return val > 1000 ? 5 : vwidth (val) + 1;
+ else
+ {
+ if ((type == SIZE || type == PAGESZ) && size_units > 0)
+ val /= size_units;
+ return vwidth (val);
+ }
+}
+
+/* Print a number of type TYPE. If SIZE_UNITS is non-zero, then values of
+ type SIZE are divided by that amount and printed without a suffix. FWIDTH
+ is the width of the field to print it in, right-justified. If SIGN is
+ true, the value is always printed with a sign, even if it's positive. */
+static void
+print_val (val_t val, enum val_type type,
+ size_t size_units, int fwidth, int sign)
+{
+ if (type == PCENT)
+ printf (sign ? "%+*d%%" : "%*d%%", fwidth - 1, val);
+ else if ((type == SIZE || type == PAGESZ) && size_units == 0)
+ {
+ float fval = val;
+ char *units = " KMGT", *u = units;
+
+ while (fval > 1024)
+ {
+ fval /= 1024;
+ u++;
+ }
+
+ printf ((fval >= 1000
+ ? (sign ? "%+*.0f%c" : "%*.0f%c")
+ : (sign ? "%+*.3g%c" : "%*.3g%c")),
+ fwidth - 1, fval, *u);
+ }
+ else
+ {
+ if ((type == SIZE || type == PAGESZ) && size_units > 0)
+ val /= size_units;
+ printf (sign ? "%+*d" : "%*d", fwidth, val);
+ }
+}
+
+/* Special values for val_t ranges. */
+#define VAL_MAX_MEM -1 /* up to the system memory size */
+#define VAL_MAX_SWAP -2 /* up to the system swap size */
+
+/* How this field changes with time. */
+enum field_change_type
+{
+ VARY, /* Can go up or down. */
+ CONST, /* Always the same. */
+ CUMUL, /* Monotonic increasing. */
+};
+
+struct vm_state; /* fwd */
+
+struct field
+{
+ /* Name of the field. */
+ char *name;
+
+ /* Terse header used for the columnar style output. */
+ char *hdr;
+
+ /* A description of this field (for user help). */
+ char *doc;
+
+ /* Type of this field. */
+ enum field_change_type change_type;
+
+ /* How to display the number associated with this field.
+ If this is anything but `DIMLESS', then it can be overriden by the
+ user. */
+ enum val_type type;
+
+ /* The `maximum value' this field can have -- used for field widths. */
+ val_t max;
+
+ /* True if we display this field by default (user can always override). */
+ int standard :1;
+
+ /* Offset of the integer_t field in a vm_statistics structure. -1 if a
+ computed field (in which case the COMPUTE field should be filled in). */
+ int offs;
+
+ /* How to compute this field. If 0, get_vmstats_value is used. This
+ function should return a negative number if there's some problem getting
+ the field. */
+ val_t (*compute)(struct vm_state *state, const struct field *field);
+};
+
+/* State about system vm from which we compute the above defined fields. */
+struct vm_state
+{
+ /* General vm statistics. */
+ struct vm_statistics vmstats;
+
+ /* default pager port (must be privileged to fetch this). */
+ mach_port_t def_pager;
+ struct default_pager_info def_pager_info;
+};
+
+static error_t
+vm_state_refresh (struct vm_state *state)
+{
+ error_t err = vm_statistics (mach_task_self (), &state->vmstats);
+
+ if (err)
+ return err;
+
+ /* Mark the info as invalid, but leave DEF_PAGER alone. */
+ bzero (&state->def_pager_info, sizeof state->def_pager_info);
+
+ return 0;
+}
+
+static val_t
+get_vmstats_field (struct vm_state *state, const struct field *field)
+{
+ val_t val =
+ (val_t)(*(integer_t *)((char *)&state->vmstats + field->offs));
+ if (field->type == SIZE)
+ val *= state->vmstats.pagesize;
+ return val;
+}
+
+static val_t
+get_size (struct vm_state *state, const struct field *field)
+{
+ return
+ (state->vmstats.free_count + state->vmstats.active_count
+ + state->vmstats.inactive_count + state->vmstats.wire_count)
+ * state->vmstats.pagesize;
+}
+
+static val_t
+vm_state_get_field (struct vm_state *state, const struct field *field)
+{
+ return (field->compute ?: get_vmstats_field) (state, field);
+}
+
+static val_t
+get_memobj_hit_ratio (struct vm_state *state, const struct field *field)
+{
+ return state->vmstats.hits * 100 / state->vmstats.lookups;
+}
+
+/* Makes sure STATE contains a default pager port and associated info, and
+ returns 0 if not (after printing an error). */
+static int
+ensure_def_pager_info (struct vm_state *state)
+{
+ error_t err;
+
+ if (state->def_pager == MACH_PORT_NULL)
+ {
+ mach_port_t host;
+
+ err = get_privileged_ports (&host, 0);
+ if (err)
+ {
+ error (0, err, "get_privileged_ports");
+ return 0;
+ }
+
+ err = vm_set_default_memory_manager (host, &state->def_pager);
+ mach_port_deallocate (mach_task_self (), host);
+
+ if (err)
+ {
+ error (0, err, "vm_set_default_memory_manager");
+ return 0;
+ }
+ }
+
+ if (!MACH_PORT_VALID (state->def_pager))
+ {
+ if (state->def_pager == MACH_PORT_NULL)
+ {
+ error (0, 0,
+ "No default pager running, so no swap information available");
+ state->def_pager = MACH_PORT_DEAD; /* so we don't try again */
+ }
+ return 0;
+ }
+
+ err = default_pager_info (state->def_pager, &state->def_pager_info);
+ if (err)
+ error (0, err, "default_pager_info");
+ return (err == 0);
+}
+
+#define SWAP_FIELD(getter, expr) \
+ static val_t getter (struct vm_state *state, const struct field *field) \
+ { return ensure_def_pager_info (state) ? (val_t) (expr) : BADVAL; }
+
+SWAP_FIELD (get_swap_size, state->def_pager_info.dpi_total_space)
+SWAP_FIELD (get_swap_free, state->def_pager_info.dpi_free_space)
+SWAP_FIELD (get_swap_page_size, state->def_pager_info.dpi_page_size)
+SWAP_FIELD (get_swap_active, (state->def_pager_info.dpi_total_space
+ - state->def_pager_info.dpi_free_space))
+
+/* Returns the byte offset of the field FIELD in a vm_statistics structure. */
+#define _F(field_name) offsetof (struct vm_statistics, field_name)
+
+#define K 1024
+#define M (1024*K)
+#define G (1024LL*M)
+
+/* vm_statistics fields we know about. */
+static const struct field fields[] =
+{
+ {"pagesize", "pgsz", "System pagesize",
+ CONST, PAGESZ, 16*K, 1, _F (pagesize) },
+ {"size", "size", "Usable physical memory",
+ CONST, SIZE, VAL_MAX_MEM, 1, 0, get_size },
+ {"free", "free", "Unused physical memory",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (free_count) },
+ {"active", "actv", "Physical memory in active use",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (active_count) },
+ {"inactive", "inact", "Physical memory in the inactive queue",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (inactive_count) },
+ {"wired", "wired", "Unpageable physical memory",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (wire_count) },
+ {"zero filled", "zeroed","Cumulative zero-filled pages",
+ CUMUL, SIZE, 90*G, 1, _F (zero_fill_count) },
+ {"reactivated", "react", "Cumulative reactivated inactive pages",
+ CUMUL, SIZE, 900*M, 1, _F (reactivations) },
+ {"pageins", "pgins", "Cumulative pages paged in",
+ CUMUL, SIZE, 90*G, 1, _F (pageins) },
+ {"pageouts", "pgouts","Cumulative pages paged out",
+ CUMUL, SIZE, 90*G, 1, _F (pageouts) },
+ {"page faults", "pfaults","Cumulative page faults",
+ CUMUL, COUNT, 99999999, 1, _F (faults) },
+ {"cow faults", "cowpfs", "Cumulative copy-on-write page faults",
+ CUMUL, COUNT, 9999999, 1, _F (cow_faults) },
+ {"memobj lookups","lkups","Memory-object lookups",
+ CUMUL, COUNT, 999999, 0, _F (lookups) },
+ {"memobj hits", "hits", "Memory-object lookups with active pagers",
+ CUMUL, COUNT, 999999, 0, _F (hits) },
+ {"memobj hit ratio","hrat","Percentage of memory-object lookups with active pagers",
+ VARY, PCENT, 99, 1, -1, get_memobj_hit_ratio },
+ {"swap size", "swsize", "Size of the default-pager swap area",
+ CONST, SIZE, VAL_MAX_SWAP, 1, 0 ,get_swap_size },
+ {"swap active", "swactv", "Default-pager swap area in use",
+ VARY, SIZE, VAL_MAX_SWAP, 0, 0 ,get_swap_active },
+ {"swap free", "swfree", "Default-pager swap area available for swapping",
+ VARY, SIZE, VAL_MAX_SWAP, 1, 0 ,get_swap_free },
+ {"swap pagesize","swpgsz", "Units used for swapping to the default pager",
+ CONST, PAGESZ, 16*K, 0, 0 ,get_swap_page_size },
+ {0}
+};
+#undef _F
+
+/* Convert a field name to the corresponding user-option. */
+static char *name_to_option (const char *name)
+{
+ char *opt = strdup (name), *p;
+ if (opt)
+ for (p = opt; *p; p++)
+ if (*p == ' ')
+ *p = '-';
+ return opt;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ const struct field *field;
+ struct vm_state state;
+ int num_fields = 0; /* Number of vm_fields known. */
+ unsigned long output_fields = 0; /* A bit per field, from 0. */
+ int count = 1; /* Number of repeats. */
+ unsigned period = 0; /* Seconds between terse mode repeats. */
+ unsigned hdr_interval = 22; /* */
+ ssize_t size_units = 0; /* -1 means `pages' */
+ int terse = 0, print_heading = 1, print_prefix = -1;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ if (key < 0)
+ /* A field option. */
+ output_fields |= (1 << (-1 - key));
+ else
+ switch (key)
+ {
+ case 't': terse = 1; break;
+ case 'p': print_prefix = 1; break;
+ case 'P': print_prefix = 0; break;
+ case 'H': print_heading = 0; break;
+ case 'b': size_units = 1; break;
+ case 'v': size_units = -1; break;
+ case 'k': size_units = K; break;
+
+ case ARGP_KEY_ARG:
+ terse = 1;
+ switch (state->arg_num)
+ {
+ case 0:
+ period = atoi (arg); count = 0; break;
+ case 1:
+ count = atoi (arg); break;
+ case 2:
+ hdr_interval = atoi (arg); break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp_option *field_opts;
+ int field_opts_size;
+ struct argp field_argp = { 0, parse_opt };
+ const struct argp_child children[] =
+ {{&field_argp, 0, "Selecting which statistics to show:"}, {0}};
+ const struct argp argp = { options, parse_opt, args_doc, doc, children };
+
+ /* See how many fields we know about. */
+ for (field = fields; field->name; field++)
+ num_fields++;
+
+ /* Construct an options vector for them. */
+ field_opts_size = ((num_fields + 1) * sizeof (struct argp_option));
+ field_opts = alloca (field_opts_size);
+ bzero (field_opts, field_opts_size);
+
+ for (field = fields; field->name; field++)
+ {
+ int which = field - fields;
+ struct argp_option *opt = &field_opts[which];
+
+ opt->name = name_to_option (field->name);
+ opt->key = -1 - which; /* options are numbered -1 ... -(N - 1). */
+ opt->doc = field->doc;
+ opt->group = 2;
+ }
+ /* No need to terminate FIELD_OPTS because the bzero above's done so. */
+
+ field_argp.options = field_opts;
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (output_fields == 0)
+ /* Show default fields. */
+ for (field = fields; field->name; field++)
+ if (field->standard)
+ output_fields |= (1 << (field - fields));
+
+ /* Returns an appropiate SIZE_UNITS for printing FIELD. */
+#define SIZE_UNITS(field) \
+ (size_units >= 0 \
+ ? size_units \
+ : ((field)->type == PAGESZ ? 0 : state.vmstats.pagesize))
+
+ /* Prints SEP if the variable FIRST is 0, otherwise, prints START (if
+ it's non-zero), and sets first to 0. */
+#define PSEP(sep, start) \
+ (first ? (first = 0, (start && fputs (start, stdout))) : fputs (sep, stdout))
+#define PVAL(val, field, width, sign) \
+ print_val (val, (field)->type, SIZE_UNITS (field), width, sign)
+ /* Intuit the likely maximum field width of FIELD. */
+#define FWIDTH(field) \
+ val_width ((field)->max == VAL_MAX_MEM ? get_size (&state, field) \
+ : (field)->max == VAL_MAX_SWAP ? get_swap_size (&state, field) \
+ : (field)->max, \
+ (field)->type, SIZE_UNITS (field))
+
+ /* Actually fetch the statistics. */
+ bzero (&state, sizeof (state)); /* Initialize STATE. */
+ err = vm_state_refresh (&state);
+ if (err)
+ error (2, err, "vm_state_refresh");
+
+ if (terse)
+ /* Terse (one-line) output mode. */
+ {
+ int first_hdr = 1, first, repeats;
+ struct vm_state prev_state;
+ int const_fields = 0;
+
+ if (count == 0)
+ count = -1;
+
+ /* We only show const fields once per page, so find out which ones
+ those are. */
+ for (field = fields; field->name; field++)
+ if ((output_fields & (1 << (field - fields)))
+ && field->change_type == CONST)
+ const_fields |= (1 << (field - fields));
+ output_fields &= ~const_fields;
+
+ if (const_fields)
+ hdr_interval--; /* Allow room for the constant fields. */
+
+ do
+ {
+ int num;
+ size_t fwidths[num_fields];
+
+ if (first_hdr)
+ first_hdr = 0;
+ else
+ putchar ('\n');
+
+ if (const_fields)
+ /* Output constant fields on a line preceding the header. */
+ {
+ for (field = fields, first = 1; field->name; field++)
+ if (const_fields & (1 << (field - fields)))
+ {
+ val_t val = vm_state_get_field (&state, field);
+ if (val < 0)
+ /* Couldn't fetch this field, don't try again. */
+ const_fields &= ~(1 << (field - fields));
+ else
+ {
+ PSEP (", ", "(");
+ printf ("%s: ", field->name);
+ PVAL (val, field, 0, 0);
+ }
+ }
+ if (! first)
+ puts (")");
+ }
+
+ /* Calculate field widths. */
+ for (field = fields, num = 0; field->name; field++, num++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ fwidths[num] = FWIDTH (field);
+ if (count != 1 && size_units == 0
+ && field->change_type == CUMUL && field->type == SIZE)
+ /* We may be printing a `+' prefix for field changes, and
+ since this is using the mostly constant-width SIZE
+ notation, individual changes may be the same width as
+ appropriated for absolute values -- so reserver another
+ column for the `+' character. */
+ fwidths[num]++;
+ if (fwidths[num] < strlen (field->hdr))
+ fwidths[num] = strlen (field->hdr);
+ }
+
+ if (print_heading)
+ {
+ for (field = fields, num = 0, first = 1; field->name; field++, num++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ PSEP (" ", 0);
+ fprintf (stdout, "%*s", fwidths[num], field->hdr);
+ }
+ putchar ('\n');
+ }
+
+ prev_state = state;
+
+ for (repeats = 0
+ ; count && repeats < hdr_interval
+ ; repeats++, count--)
+ {
+ /* Output the fields. */
+ for (field = fields, num = 0, first = 1; field->name; field++, num++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ val_t val = vm_state_get_field (&state, field);
+
+ if (val < 0)
+ /* Couldn't fetch this field, don't try again. */
+ const_fields &= ~(1 << (field - fields));
+ else
+ {
+ int sign = 0;
+
+ if (repeats && field->change_type == CUMUL)
+ {
+ sign = 1;
+ val -= vm_state_get_field (&prev_state, field);
+ }
+
+ PSEP (" ", 0);
+ PVAL (val, field, fwidths[num], sign);
+ }
+ }
+ putchar ('\n');
+
+ prev_state = state;
+
+ if (period)
+ {
+ sleep (period);
+ err = vm_state_refresh (&state);
+ if (err)
+ error (2, err, "vm_state_refresh");
+ }
+ }
+ }
+ while (count);
+ }
+ else
+ /* Verbose output. */
+ {
+ int max_width = 0;
+
+ if (print_prefix < 0)
+ /* By default, only print a prefix if there are multiple fields. */
+ print_prefix = (output_fields & (output_fields - 1));
+
+ if (print_prefix)
+ /* Find the widest description string, so we can align the output. */
+ for (field = fields; field->name; field++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ int width = strlen (field->name) + FWIDTH (field);
+ if (width > max_width)
+ max_width = width;
+ }
+
+ for (field = fields; field->name; field++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ val_t val = vm_state_get_field (&state, field);
+ if (val >= 0)
+ {
+ int fwidth = 0;
+ if (print_prefix)
+ {
+ printf ("%s: ", field->name);
+ fwidth = max_width - strlen (field->name);
+ }
+ PVAL (val, field, fwidth, 0);
+ putchar ('\n');
+ }
+ }
+ }
+
+ exit (0);
+}
diff --git a/utils/w.c b/utils/w.c
new file mode 100644
index 00000000..1ffbaaa9
--- /dev/null
+++ b/utils/w.c
@@ -0,0 +1,519 @@
+/* Hurdish w
+
+ Copyright (C) 1995,96,97,98,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 <string.h>
+#include <unistd.h>
+#include <paths.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <version.h>
+
+#include <sys/fcntl.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <envz.h>
+#include <idvec.h>
+#include <ps.h>
+#include <timefmt.h>
+#include <error.h>
+
+#include "psout.h"
+
+#define DEFAULT_FMT_STRING "%^%user %tty %from %login %idle %pid %what"
+
+extern char *canon_host (char *host);
+extern char *shared_domain (char *host1, char *host2);
+extern char *localhost ();
+
+const char *argp_program_version = STANDARD_HURD_VERSION (w);
+
+#define OA OPTION_ARG_OPTIONAL
+
+static struct argp_option options[] =
+{
+ {"format", 'F', "FMT", 0, "Use the custom output-format FMT"},
+ {"no-header", 'H', 0, 0, "Don't print a descriptive header line"},
+ {0, 'h', 0, OPTION_ALIAS | OPTION_HIDDEN}, /* BSD compat */
+ {"reverse", 'r', 0, 0, "Reverse the order of any sort"},
+ {"sort", 's', "FIELD",0, "Sort the output with respect to FIELD,"
+ " backwards if FIELD is prefixed by `-'"},
+ {0, 'i', 0, 0, "Sort output by idle time"},
+ {"tty", 't', "TTY", OA, "Only show entries for terminal TTY"},
+ {"width", 'w', "WIDTH",OA, "If WIDTH is given, try to format the"
+ " output for WIDTH columns, otherwise,"
+ " remove the default limit"},
+ {"uptime", 'u', 0, 0, "Only show the uptime and load info"},
+ {"no-uptime", 'U', 0, 0, "Don't show the uptime and load info"},
+ {"raw-hosts", 'n', 0, 0, "Show network addresses as numbers"},
+ {0, 0}
+};
+static char *args_doc = "[USER...]";
+static char *doc = "Show logged in users and what they are doing";
+
+/* The current time of day. */
+static struct timeval now;
+
+/* True if we shouldn't attempt to show names for host addresses. */
+static int raw_hosts = 0;
+
+struct w_hook
+{
+ struct utmp utmp;
+ struct timeval idle;
+ struct ps_user *user;
+ char *host; /* A malloced host name. */
+};
+
+#define W_PSTAT_USER (PSTAT_USER_BASE << 0)
+#define W_PSTAT_HOST (PSTAT_USER_BASE << 1)
+#define W_PSTAT_IDLE (PSTAT_USER_BASE << 2)
+#define W_PSTAT_LOGIN (PSTAT_USER_BASE << 3)
+
+static ps_flags_t
+w_deps (ps_flags_t flags)
+{
+ if (flags & W_PSTAT_IDLE)
+ flags |= PSTAT_TTY;
+ return flags;
+}
+
+static ps_flags_t
+w_fetch (struct proc_stat *ps, ps_flags_t need, ps_flags_t have)
+{
+ struct w_hook *hook = ps->hook;
+
+ if (! hook)
+ return 0; /* Can't do anything without w specific info.*/
+
+ if (need & W_PSTAT_HOST)
+ {
+ struct utmp *utmp = &hook->utmp;
+ if (utmp->ut_host[0])
+ {
+ /* UTMP->ut_host might not be '\0' terminated; this copy is. */
+ char ut_host[sizeof utmp->ut_host + 1];
+
+ strncpy (ut_host, utmp->ut_host, sizeof utmp->ut_host);
+ ut_host[sizeof utmp->ut_host] = '\0';
+
+ if (raw_hosts)
+ hook->host = strdup (ut_host);
+ else
+ {
+ char *sd;
+ hook->host = strdup (canon_host (ut_host) ?: ut_host);
+ sd = shared_domain (hook->host, localhost ());
+ if (sd)
+ *sd = '\0';
+ }
+ }
+ have |= W_PSTAT_HOST;
+ }
+
+ if (need & W_PSTAT_IDLE)
+ {
+ if (have & PSTAT_TTY)
+ {
+ struct stat stat;
+ struct ps_tty *tty = ps->tty;
+
+ hook->idle.tv_usec = 0;
+ if (! tty)
+ {
+ hook->idle.tv_sec = 0;
+ have |= W_PSTAT_IDLE;
+ }
+ else
+ {
+ if (io_stat (tty->port, &stat) == 0)
+ {
+ hook->idle.tv_sec = now.tv_sec - stat.st_atime;
+ have |= W_PSTAT_IDLE;
+ }
+ }
+ }
+ else if (ps->inapp & PSTAT_TTY)
+ ps->inapp |= W_PSTAT_IDLE;
+ }
+
+ if (need & W_PSTAT_USER)
+ if (ps_user_uname_create (hook->utmp.ut_name, &hook->user) == 0)
+ have |= W_PSTAT_USER;
+
+ /* We can always return these. */
+ have |= (need & W_PSTAT_LOGIN);
+
+ return have;
+}
+
+static void
+w_cleanup (struct proc_stat *ps)
+{
+ if (ps->hook)
+ {
+ if (ps->flags & W_PSTAT_HOST)
+ free (((struct w_hook *)ps->hook)->host);
+ free (ps->hook);
+ }
+}
+
+static void
+w_get_idle (struct proc_stat *ps, struct timeval *tv)
+{
+ struct w_hook *hook = ps->hook;
+ *tv = hook->idle;
+}
+const struct ps_getter w_idle_getter =
+{"idle_time", W_PSTAT_IDLE, w_get_idle};
+
+static void
+w_get_login (struct proc_stat *ps, struct timeval *tv)
+{
+ struct w_hook *hook = ps->hook;
+ tv->tv_usec = 0;
+ tv->tv_sec = hook ? hook->utmp.ut_time : 0;
+}
+const struct ps_getter w_login_getter =
+{"login_time", 0, w_get_login};
+
+static void
+w_get_uname (struct proc_stat *ps, char **uname, unsigned *uname_len)
+{
+ struct w_hook *hook = ps->hook;
+ *uname = hook->utmp.ut_name;
+ *uname_len = ((char *)memchr (*uname, 0, UT_NAMESIZE) ?: *uname) - *uname;
+}
+const struct ps_getter w_uname_getter =
+{"uname", 0, w_get_uname};
+
+static struct ps_user *
+w_get_user (struct proc_stat *ps)
+{
+ struct w_hook *hook = ps->hook;
+ return hook->user;
+}
+const struct ps_getter w_user_getter =
+{"user", W_PSTAT_USER, (void (*)())w_get_user};
+
+static void
+w_get_host (struct proc_stat *ps, char **host, unsigned *host_len)
+{
+ struct w_hook *hook = ps->hook;
+ *host = hook->host;
+ *host_len = *host ? strlen (*host) : 0;
+}
+const struct ps_getter w_host_getter =
+{"host", W_PSTAT_HOST, w_get_host};
+
+extern error_t ps_emit_past_time (), ps_emit_string (), ps_emit_minutes ();
+extern error_t ps_emit_user_name ();
+extern int ps_cmp_times (), ps_cmp_strings (), ps_cmp_unames ();
+extern int ps_nominal_string ();
+const struct ps_fmt_spec _w_specs[] =
+{
+ {"User", 0, 8, -1,0, &w_uname_getter,ps_emit_string, ps_cmp_strings},
+ {"Name", 0, 16, -1,0, &w_user_getter, ps_emit_user_name,ps_cmp_unames,ps_nominal_string},
+ {"Login","Login@", -7, -1,0,&w_login_getter,ps_emit_past_time,ps_cmp_times},
+ {"From", 0, 14, -1,0, &w_host_getter, ps_emit_string, ps_cmp_strings, ps_nominal_string},
+ {"Idle", 0, -5, -1,PS_FMT_FIELD_COLON_MOD, &w_idle_getter, ps_emit_minutes,ps_cmp_times},
+ {"What=args"},
+ {0}
+};
+struct ps_fmt_specs w_specs = {_w_specs, &ps_std_fmt_specs};
+
+/* Add to PROCS any processes in the foreground process group corresponding
+ to U, attaching a struct w_hook to which ever process is deemed the most
+ noteworthy. */
+static void
+add_utmp_procs (struct proc_stat_list *procs, struct utmp *u)
+{
+ /* The tty name, with space for '\0' termination and an
+ appropiate prefix. */
+ char tty[sizeof _PATH_DEV + sizeof u->ut_line];
+ io_t tty_node;
+ error_t err;
+ pid_t pid;
+ int pos;
+ struct proc_stat *ps;
+
+ switch (u->ut_type)
+ {
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ /* These are the types that indicate a user job that we might
+ find processes for. */
+ if (u->ut_name[0] != '\0' && u->ut_line[0] != '\0')
+ break;
+ default:
+ /* This entry is not for a user, skip it. */
+ return;
+ }
+
+ strncpy (tty, u->ut_line, sizeof u->ut_line);
+ tty[sizeof u->ut_line] = '\0'; /* Ensure it's '\0' terminated. */
+
+ if (*tty != '/')
+ /* Not an absolute path -- it must be in /dev, which insert. */
+ {
+ bcopy (tty, tty + sizeof _PATH_DEV - 1, strlen (tty) + 1);
+ bcopy (_PATH_DEV, tty, sizeof _PATH_DEV - 1);
+ }
+
+ /* Now find which process group is the in foreground for TTY. */
+ tty_node = file_name_lookup (tty, 0, 0);
+ if (tty_node == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", tty);
+ return;
+ }
+
+ err = io_get_owner (tty_node, &pid);
+ if (err)
+ {
+ error (0, err, "%s", tty);
+ return;
+ }
+
+ /* The new process will get added at the end, so look for it there. */
+ pos = proc_stat_list_num_procs (procs);
+ if (pid >= 0)
+ err = proc_stat_list_add_pid (procs, pid, &ps);
+ else
+ {
+ struct proc_stat **pgrp_procs;
+ unsigned num_procs;
+
+ err = proc_stat_list_add_pgrp (procs, -pid, &pgrp_procs, &num_procs);
+ if (! err)
+ {
+ if (num_procs > 0)
+ ps = pgrp_procs[0]; /* Use the first one. */
+ else
+ ps = 0;
+ free (pgrp_procs);
+ }
+ }
+ if (err)
+ {
+ error (0, err, "%s (owner %s %d)",
+ tty, pid < 0 ? "pgrp" : "pid", pid < 0 ? -pid : pid);
+ return;
+ }
+
+ if (ps)
+ {
+ ps->hook = malloc (sizeof (struct w_hook));
+ if (ps->hook)
+ bcopy (u, &((struct w_hook *)ps->hook)->utmp, sizeof *u);
+ }
+ else
+ error (0, 0, "%s (owner %s %d): No processes",
+ tty, pid < 0 ? "pgrp" : "pid", pid < 0 ? -pid : pid);
+}
+
+/* Find the absolute timestamp of when the system was booted.
+ We define "system boot time" as the task creation time of PID 1 (init). */
+
+static error_t
+fetch_boot_time (struct timeval *when)
+{
+ struct ps_context *context;
+ struct proc_stat *ps;
+ error_t err;
+
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ error (2, err, "ps_context_create");
+
+ err = ps_context_find_proc_stat (context, 1, &ps);
+ if (err)
+ error (3, err, "ps_context_find_proc_stat");
+
+ err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC);
+ if (!err && !(ps->flags & PSTAT_TASK_BASIC))
+ err = EGRATUITOUS;
+ if (err)
+ {
+ error (0, err, "cannot find boot time");
+ return err;
+ }
+ else
+ {
+ time_value_t *const tv = &proc_stat_task_basic_info (ps)->creation_time;
+ when->tv_sec = tv->seconds;
+ when->tv_usec = tv->microseconds;
+ }
+
+ ps_context_free (context);
+
+ return 0;
+}
+
+static void
+uptime (struct proc_stat_list *procs)
+{
+ error_t err;
+ struct timeval boot_time;
+ char uptime_rep[20], tod_rep[20];
+ struct host_load_info *load;
+ unsigned nusers = 0;
+ int maybe_add_user (struct proc_stat *ps)
+ { if (ps->hook) nusers++; return 0; }
+
+ proc_stat_list_for_each (procs, maybe_add_user);
+
+ if (fetch_boot_time (&boot_time))
+ strcpy (uptime_rep, "chuck");
+ else
+ {
+ struct timeval uptime;
+ timersub (&now, &boot_time, &uptime);
+ fmt_named_interval (&uptime, 0, uptime_rep, sizeof (uptime_rep));
+ }
+
+ strftime (tod_rep, sizeof (tod_rep), "%r",
+ localtime ((time_t *)&now.tv_sec));
+ if (tod_rep[0] == '0')
+ tod_rep[0] = ' '; /* Get rid of bletcherous leading 0. */
+
+ err = ps_host_load_info (&load);
+ if (err)
+ error (0, err, "ps_host_load_info");
+
+ printf ("%s up %s, %u user%s, load averages: %.2f, %.2f, %.2f\n",
+ tod_rep, uptime_rep, nusers, nusers == 1 ? "" : "s",
+ (double)load->avenrun[0] / (double)LOAD_SCALE,
+ (double)load->avenrun[1] / (double)LOAD_SCALE,
+ (double)load->avenrun[2] / (double)LOAD_SCALE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ struct utmp *ut;
+ struct ps_context *context;
+ int output_width = -1;
+ char *fmt_string = DEFAULT_FMT_STRING, *sort_key_name = NULL;
+ int sort_reverse = 0, print_heading = 1, show_entries = 1, show_uptime = 1;
+ int squash_bogus_fields = 1, squash_nominal_fields = 1;
+ struct proc_stat_list *procs;
+#if 0
+ char *tty_names = 0;
+ unsigned num_tty_names = 0;
+#endif
+ uid_t *users = 0;
+ size_t num_users = 0;
+ struct ps_user_hooks ps_hooks = { w_deps, w_fetch, w_cleanup };
+
+ int has_hook (struct proc_stat *ps) { return ps->hook != 0; }
+
+ int keep_users (struct proc_stat *ps)
+ {
+ int i;
+ struct w_hook *h = ps->hook;
+ for (i = 0; i < num_users; i++)
+ if (users[i] == h->user->uid)
+ return 1;
+ return 0;
+ }
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'H': case 'h': print_heading = 0; break;
+ case 'i': sort_key_name = "idle"; break;
+ case 's': sort_key_name = arg; break;
+ case 'F': fmt_string = arg; break;
+ case 'r': sort_reverse = 1; break;
+ case 'u': show_entries = 0; break;
+ case 'U': show_uptime = 0; break;
+ case 'n': raw_hosts = 1; break;
+ case 'w': output_width = arg ? atoi (arg) : 0; break;
+
+ case ARGP_KEY_ARG:
+ num_users++;
+ users = realloc (users, num_users * sizeof (*users));
+ if (! users)
+ argp_failure (state, 5, ENOMEM, "%s", arg);
+ else if (isdigit (*arg))
+ users[num_users - 1] = atoi (arg);
+ else
+ {
+ struct passwd *pw = getpwnam (arg);
+ if (! pw)
+ argp_failure (state, 6, 0, "%s: Unknown user", arg);
+ users[num_users - 1] = pw->pw_uid;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ if (gettimeofday (&now, 0) < 0)
+ error (0, errno, "gettimeofday");
+
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ error (2, err, "ps_context_create");
+
+ err = proc_stat_list_create (context, &procs);
+ if (err)
+ error (3, err, "proc_stat_list_create");
+ context->user_hooks = &ps_hooks;
+
+ /* Parse our options. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ /* Read the utmp file. */
+ setutent ();
+ while ((ut = getutent ()) != NULL)
+ add_utmp_procs (procs, ut);
+ endutent ();
+
+ /* Keep only processes that have our hooks attached. */
+ proc_stat_list_filter1 (procs, has_hook, 0, 0);
+
+ if (num_users > 0)
+ proc_stat_list_filter1 (procs, keep_users, W_PSTAT_USER, 0);
+
+ if (show_uptime)
+ uptime (procs);
+
+ if (show_entries)
+ psout (procs, fmt_string, 0, &w_specs, sort_key_name, sort_reverse,
+ output_width, print_heading,
+ squash_bogus_fields, squash_nominal_fields, 0);
+
+ return 0;
+}
diff --git a/utils/x.c b/utils/x.c
new file mode 100644
index 00000000..e4e272f4
--- /dev/null
+++ b/utils/x.c
@@ -0,0 +1,248 @@
+/* Hurdish su
+
+ Copyright (C) 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 <string.h>
+#include <unistd.h>
+#include <paths.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/fcntl.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <envz.h>
+#include <idvec.h>
+#include <error.h>
+#include <timefmt.h>
+#include <hurd/lookup.h>
+
+static struct argp_option options[] =
+{
+ {"add", 'a', "USER", 0, "Add following ids"},
+ {"remove", 'r', "USER", 0, "Remove following ids"},
+ {"user", 'u', "USER", 0, "Add USER to the effective uids"},
+ {"avail-user",'U', "USER", 0, "Add USER to the available uids"},
+ {"group", 'g', "GROUP", 0, "Add GROUP to the effective groups"},
+ {"avail-group",'G',"GROUP", 0, "Add GROUP to the available groups"},
+ {0, 0}
+};
+static char *args_doc = "[USER...]";
+static char *doc = "Modify authentication of existing processes"
+ "\vA USER specified as an argument adds (or removes) that user's groups as"
+ " well. When removing groups implied by such an argument, the groups to"
+ " which uids remaining in the process after any we remove are ignored."
+"\nUids and groups specified with options are used as-is.";
+
+/* Full set of desired authorization. XXX msg_del_auth doesn't allow such
+ fine control. */
+struct auth
+{
+ struct idvec euids, egids; /* Effective ids. */
+ struct idvec auids, agids; /* Available ids. */
+};
+
+/* Ids of our parent process, with the effect of this program being su'd
+ removed. */
+static struct idvec parent_uids = {0}, parent_gids = {0};
+
+/* Make sure that the parent_[ug]ids are filled in. To make them useful for
+ su'ing, each is the avail ids with all effective ids but the first
+ appended; this gets rid of the effect of login being suid, and is useful
+ as the new process's avail id list (e.g., the real id is right). */
+static void
+need_parent_ids ()
+{
+ if (parent_uids.num == 0 && parent_gids.num == 0)
+ {
+ struct idvec *p_eff_uids = make_idvec ();
+ struct idvec *p_eff_gids = make_idvec ();
+ if (!p_eff_uids || !p_eff_gids)
+ err = ENOMEM;
+ if (! err)
+ err = idvec_merge_auth (p_eff_uids, parent_uids,
+ p_eff_gids, parent_gids,
+ parent_auth);
+ if (! err)
+ {
+ idvec_delete (p_eff_uids, 0); /* Counteract setuid. */
+ idvec_delete (p_eff_gids, 0);
+ err = idvec_merge (parent_uids, p_eff_uids);
+ if (! err)
+ err = idvec_merge (parent_gids, p_eff_gids);
+ }
+ if (err)
+ error (39, err, "Can't get uids");
+ }
+}
+
+/* Returns true if the *caller* of this login program has UID. */
+static int
+parent_has_uid (uid_t uid)
+{
+ need_parent_ids ();
+ return idvec_contains (parent_uids, uid);
+}
+/* Returns true if the *caller* of this login program has GID. */
+static int
+parent_has_gid (gid_t gid)
+{
+ need_parent_ids ();
+ return idvec_contains (parent_gids, gid);
+}
+/* Returns the number of parent uids. */
+static int
+count_parent_uids ()
+{
+ need_parent_ids ();
+ return parent_uids.num;
+}
+/* Returns the number of parent gids. */
+static int
+count_parent_gids ()
+{
+ need_parent_ids ();
+ return parent_gids.num;
+}
+
+/* Make sure the user should be allowed to do this. */
+void verify_passwd (const char *name, const char *password,
+ uid_t id, int is_group, structh auth *auth)
+{
+ extern char *crypt (const char salt[2], const char *string);
+ char *prompt, *unencrypted, *encrypted;
+
+ if (!password || !*password
+ || idvec_contains (is_group ? auth->egids : auth->euids, id)
+ || idvec_contains (is_group ? auth->agids : auth->auids, id)
+ || (no_passwd
+ && (parent_has_uid (0)
+ || (is_group ? parent_has_uid (id) : parent_has_gid (id)))))
+ return; /* Already got this one. */
+
+ if (name)
+ asprintf (&prompt, "Password for %s%s:",
+ is_group ? "group " : "", name);
+ else
+ prompt = "Password:";
+
+ unencrypted = getpass (prompt);
+ encrypted = crypt (unencrypted, password);
+ /* Paranoia may destroya. */
+ memset (unencrypted, 0, strlen (unencrypted));
+
+ if (name)
+ free (prompt);
+
+ if (strcmp (encrypted, password) != 0)
+ error (50, 0, "Incorrect password", 0);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int i;
+ error_t err = 0;
+ struct auth add, remove;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_NO_ARGS:
+ arg = "0"; /* root */
+ /* fall through. */
+
+ case 'u':
+ case 'U':
+ {
+ struct passwd *pw =
+ isdigit (*user) ? getpwuid (atoi (user)) : getpwnam (user);
+ /* True if this is the user arg and there were no user options. */
+
+ if (! pw)
+ error (10, 0, "%s: Unknown user", user);
+
+ verify_passwd (state->argv[state->next] ? pw->pw_name : 0,
+ pw->pw_passwd, pw->pw_uid, 0, &auth);
+
+ if (key == 'u')
+ idvec_add_new (&auth.euids, pw->pw_uid);
+ else if (key == 'U')
+ /* Add available ids instead of effective ones. */
+ idvec_add_new (&auth.auids, pw->pw_uid);
+ else
+ /* A plain argument. Add both the specified user and any
+ associated groups. */
+ {
+ /* Effective */
+ idvec_add_new (&auth.euids, 0, pw->pw_uid);
+ idvec_add_new (&auth.egids, 0, pw->pw_gid);
+ }
+ }
+ break;
+
+ case 'g':
+ case 'G':
+ {
+ struct group *gr =
+ isdigit (*arg) ? getgrgid (atoi (arg)) : getgrnam (arg);
+ if (! gr)
+ error (11, 0, "%s: Unknown group", arg);
+ verify_passwd (gr->gr_name, gr->gr_passwd, gr->gr_gid, 1, &auth);
+ idvec_add_new (key == 'g' ? &auth.egids : &auth.agids, gr->gr_gid);
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ bzero (add, sizeof add);
+ bzero (remove, sizeof remove);
+
+
+ err =
+ auth_makeauth (getauth (), 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ &auth.euids->ids, &auth.euids->num,
+ &auth.auids->ids, &auth.auids->num,
+ &auth.egids->ids, &auth.egids->num,
+ &auth.agids->ids, &auth.agids->num,
+ &auth);
+ if (err)
+ error (3, err, "Authentication failure", 0);
+
+
+ exit(0);
+}