diff options
Diffstat (limited to 'utils')
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, ¶ms, &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, ¶ms, &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); +} |