diff options
Diffstat (limited to 'utils')
50 files changed, 7357 insertions, 3140 deletions
diff --git a/utils/ChangeLog b/utils/ChangeLog deleted file mode 100644 index c2222747..00000000 --- a/utils/ChangeLog +++ /dev/null @@ -1,1152 +0,0 @@ -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 index ebbc8d06..3f3307f4 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,5 +1,5 @@ -# -# Copyright (C) 1994, 1995, 1996 Free Software Foundation +# +# Copyright (C) 1994,95,96,97,98,99,2000,01,02 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 @@ -18,36 +18,66 @@ dir := utils makemode := utilities -targets = shd ps settrans showtrans sync su mount fsysopts \ - storeinfo login w uptime hurdids loginpr sush vmstat portinfo \ - devprobe -special-targets = mount loginpr sush uptime -SRCS = shd.c ps.c su.c settrans.c sync.c showtrans.c \ - mount.sh fsysopts.c storeinfo.c login.c loginpr.sh sush.sh w.c \ - uptime.sh psout.c hurdids.c vmstat.c portinfo.c devprobe.c - -OBJS = $(filter-out loginpr.sh mount.sh sush.sh uptime.sh,$(SRCS:.c=.o)) +targets = shd ps settrans showtrans syncfs fsysopts \ + storeinfo login w uptime ids loginpr sush vmstat portinfo \ + devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \ + storeread msgport rpctrace mount gcore fakeauth fakeroot +special-targets = loginpr sush uptime fakeroot +SRCS = shd.c ps.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 msgport.c \ + rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh +LCLHDRS = psout.h parse.h pids.h frobauth.h -# This hack to get around ickiness in Makeconf -libutil-libsubst = -lutil +OBJS = $(filter-out %.sh,$(SRCS:.c=.o)) +HURDLIBS = ps ihash store fshelp threads ports ftpconn shouldbeinlibc +login-LDLIBS = -lutil $(LIBCRYPT) +addauth-LDLIBS = $(LIBCRYPT) +setauth-LDLIBS = $(LIBCRYPT) 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 include ../Makeconf -login: -lutil # /gd4/hurdinst/lib/libutil.a # ick ick ick ick +ps addauth rmauth setauth unsu msgport: parse.o pids.o +login addauth setauth: nonsugid.o +addauth rmauth setauth unsu: frobauth.o +rmauth setauth unsu: frobauth-mod.o ps w: psout.o ../libps/libps.a ../libihash/libihash.a -storeinfo: ../libstore/libstore.a + +storeinfo storecat storeread: ../libstore/libstore.a +ftpcp ftpdir: ../libftpconn/libftpconn.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 hurdids settrans showtrans fsysopts storeinfo login vmstat portinfo \ - devprobe: ../libshouldbeinlibc/libshouldbeinlibc.a - -%: %.sh - cp $< $@ +ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \ + devprobe vminfo addauth rmauth setauth unsu ftpcp ftpdir storeread \ + storecat msgport mount: \ + ../libshouldbeinlibc/libshouldbeinlibc.a $(filter-out $(special-targets), $(targets)): %: %.o + +rpctrace: ../libthreads/libthreads.a \ + ../libports/libports.a ../libihash/libihash.a +rpctrace-CPPFLAGS = -DDATADIR=\"${datadir}\" + +fakeauth: authServer.o auth_requestUser.o interruptServer.o \ + ../libthreads/libthreads.a \ + ../libports/libports.a ../libihash/libihash.a \ + ../libshouldbeinlibc/libshouldbeinlibc.a +auth-MIGSFLAGS = -imacros $(srcdir)/../auth/authmutations.h +fakeauth-CPPFLAGS = -I$(srcdir)/../auth +authServer-CPPFLAGS = -I$(srcdir)/../auth +auth_requestUser-CPPFLAGS = -I$(srcdir)/../auth + +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 index e29e1e32..d7020322 100644 --- a/utils/devprobe.c +++ b/utils/devprobe.c @@ -1,6 +1,6 @@ /* Check the existence of mach devices - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc. Written by Miles Bader <miles@gnu.ai.mit.edu> @@ -20,11 +20,13 @@ #include <stdio.h> #include <argp.h> -#include <error.h> #include <hurd.h> #include <mach.h> #include <device/device.h> +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (devprobe); static const struct argp_option options[] = { {"silent", 's', 0, 0, "Don't print devices found"}, @@ -33,19 +35,19 @@ static const struct argp_option options[] = { {0} }; static const char *args_doc = "DEVNAME..."; -static const char *doc = "The exit status is 0 if any devices were found."; +static const char *doc = "Test for the existence of mach device DEVNAME..." + "\vThe exit status is 0 if any devices were found."; int main (int argc, char **argv) { - error_t err; - mach_port_t device_master; /* 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) @@ -65,10 +67,19 @@ main (int argc, char **argv) all = 0; break; case ARGP_KEY_ARG: - err = device_open (device_master, 0, arg, &device); + 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. */ { + device_close (device); + /* Free the device port we got. */ mach_port_deallocate (mach_task_self (), device); @@ -84,7 +95,7 @@ main (int argc, char **argv) } else if (err != ED_NO_SUCH_DEVICE) /* Complain about unexpected errors. */ - error (0, err, "%s", arg); + argp_failure (state, 0, err, "%s", arg); break; default: @@ -94,10 +105,6 @@ main (int argc, char **argv) } const struct argp argp = { options, parse_opt, args_doc, doc }; - err = get_privileged_ports (0, &device_master); - if (err) - error (3, err, "Can't get device master port"); - /* Parse our arguments. */ argp_parse (&argp, argc, argv, 0, 0, 0); diff --git a/utils/fakeauth.c b/utils/fakeauth.c new file mode 100644 index 00000000..49fa7f1c --- /dev/null +++ b/utils/fakeauth.c @@ -0,0 +1,434 @@ +/* fakeauth -- proxy auth server to lie to users about what their IDs are + Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <hurd.h> +#include <hurd/auth.h> +#include <hurd/interrupt.h> +#include <hurd/ports.h> +#include <idvec.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <assert.h> +#include <argp.h> +#include <error.h> +#include "auth_S.h" +#include "auth_request_U.h" +#include "interrupt_S.h" + +/* Auth handles are server ports with sets of ids. */ +struct authhandle + { + struct port_info pi; + struct idvec euids, egids, auids, agids; + }; + +struct port_bucket *auth_bucket; +struct port_class *authhandle_portclass; + +/* Create a new auth port. */ + +static error_t +create_authhandle (struct authhandle **new) +{ + error_t err = ports_create_port (authhandle_portclass, auth_bucket, + sizeof **new, new); + if (! err) + bzero (&(*new)->euids, (void *) &(*new)[1] - (void *) &(*new)->euids); + return err; +} + +/* Clean up a dead auth port. */ + +static void +destroy_authhandle (void *p) +{ + struct authhandle *h = p; + idvec_free_contents (&h->euids); + idvec_free_contents (&h->egids); + idvec_free_contents (&h->auids); + idvec_free_contents (&h->agids); +} + +/* Called by server stub functions. */ + +authhandle_t +auth_port_to_handle (auth_t auth) +{ + return ports_lookup_port (auth_bucket, auth, authhandle_portclass); +} + +/* id management. */ + +static inline void +idvec_copyout (struct idvec *idvec, uid_t **ids, size_t *nids) +{ + if (idvec->num > *nids) + *ids = idvec->ids; + else + memcpy (*ids, idvec->ids, idvec->num * sizeof *ids); + *nids = idvec->num; +} + +#define C(auth, ids) idvec_copyout (&auth->ids, ids, n##ids) +#define OUTIDS(auth) (C (auth, euids), C (auth, egids), \ + C (auth, auids), C (auth, agids)) + +/* Implement auth_getids as described in <hurd/auth.defs>. */ +kern_return_t +S_auth_getids (struct authhandle *auth, + uid_t **euids, + size_t *neuids, + uid_t **auids, + size_t *nauids, + uid_t **egids, + size_t *negids, + uid_t **agids, + size_t *nagids) +{ + if (! auth) + return EOPNOTSUPP; + + OUTIDS (auth); + + return 0; +} + +/* Implement auth_makeauth as described in <hurd/auth.defs>. */ +kern_return_t +S_auth_makeauth (struct authhandle *auth, + mach_port_t *authpts, size_t nauths, + uid_t *euids, size_t neuids, + uid_t *auids, size_t nauids, + uid_t *egids, size_t negids, + uid_t *agids, size_t nagids, + mach_port_t *newhandle) +{ + struct authhandle *newauth, *auths[1 + nauths]; + int hasroot = 0; + error_t err; + size_t i, j; + + if (!auth) + return EOPNOTSUPP; + + auths[0] = auth; + + /* Fetch the auth structures for all the ports passed in. */ + for (i = 0; i < nauths; i++) + auths[i + 1] = auth_port_to_handle (authpts[i]); + + ++nauths; + + /* Verify that the union of the handles passed in either contains euid 0 + (root), or contains all the requested ids. */ + +#define isuid(uid, auth) \ + (idvec_contains (&(auth)->euids, uid) \ + || idvec_contains (&(auth)->auids, uid)) +#define groupmember(gid, auth) \ + (idvec_contains (&(auth)->egids, gid) \ + || idvec_contains (&(auth)->agids, gid)) +#define isroot(auth) isuid (0, auth) + + for (i = 0; i < nauths; i++) + if (auths[i] && isroot (auths[i])) + { + hasroot = 1; + break; + } + + if (!hasroot) + { + int has_it; + + for (i = 0; i < neuids; i++) + { + has_it = 0; + for (j = 0; j < nauths; j++) + if (auths[j] && isuid (euids[i], auths[j])) + { + has_it = 1; + break; + } + if (!has_it) + goto eperm; + } + + for (i = 0; i < nauids; i++) + { + has_it = 0; + for (j = 0; j < nauths; j++) + if (auths[j] && isuid (auids[i], auths[j])) + { + has_it = 1; + break; + } + if (!has_it) + goto eperm; + } + + for (i = 0; i < negids; i++) + { + has_it = 0; + for (j = 0; j < nauths; j++) + if (auths[j] && groupmember (egids[i], auths[j])) + { + has_it = 1; + break; + } + if (!has_it) + goto eperm; + } + + for (i = 0; i < nagids; i++) + { + has_it = 0; + for (j = 0; j < nauths; j++) + if (auths[j] && groupmember (agids[i], auths[j])) + { + has_it = 1; + break; + } + if (!has_it) + goto eperm; + } + } + + err = create_authhandle (&newauth); + + /* Create a new handle with the specified ids. */ + +#define MERGE S (euids); S (egids); S (auids); S (agids); +#define S(uids) if (!err) err = idvec_merge_ids (&newauth->uids, uids, n##uids) + + MERGE; + +#undef S + + if (! err) + { + for (j = 1; j < nauths; ++j) + mach_port_deallocate (mach_task_self (), authpts[j - 1]); + *newhandle = ports_get_right (newauth); + ports_port_deref (newauth); + } + + for (j = 1; j < nauths; j++) + if (auths[j]) + ports_port_deref (auths[j]); + return err; + + eperm: + for (j = 1; j < nauths; j++) + if (auths[j]) + ports_port_deref (auths[j]); + return EPERM; +} + + +/* This is our original auth port, where we will send all proxied + authentication requests. */ +static auth_t real_auth_port; + +kern_return_t +S_auth_user_authenticate (struct authhandle *userauth, + mach_port_t reply, + mach_msg_type_name_t reply_type, + mach_port_t rendezvous, + mach_port_t *newport, + mach_msg_type_name_t *newporttype) +{ + if (! userauth) + return EOPNOTSUPP; + + if (rendezvous == MACH_PORT_DEAD) /* Port died in transit. */ + return EINVAL; + + return auth_user_authenticate_request (real_auth_port, reply, reply_type, + rendezvous, MACH_MSG_TYPE_MOVE_SEND) + ? : MIG_NO_REPLY; +} + +kern_return_t +S_auth_server_authenticate (struct authhandle *serverauth, + mach_port_t reply, + mach_msg_type_name_t reply_type, + mach_port_t rendezvous, + mach_port_t newport, + mach_msg_type_name_t newport_type, + uid_t **euids, + size_t *neuids, + uid_t **auids, + size_t *nauids, + uid_t **egids, + size_t *negids, + uid_t **agids, + size_t *nagids) +{ + if (! serverauth) + return EOPNOTSUPP; + + if (rendezvous == MACH_PORT_DEAD) /* Port died in transit. */ + return EINVAL; + + return auth_server_authenticate_request (real_auth_port, + reply, reply_type, + rendezvous, MACH_MSG_TYPE_MOVE_SEND, + newport, newport_type) + ? : MIG_NO_REPLY; +} + +kern_return_t +S_interrupt_operation (mach_port_t port, mach_port_seqno_t seqno) +{ + return interrupt_operation (real_auth_port, 0); +} + +static int +auth_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + extern int auth_server (mach_msg_header_t *inp, mach_msg_header_t *outp); + extern int interrupt_server (mach_msg_header_t *inp, mach_msg_header_t *); + return (auth_server (inp, outp) || + interrupt_server (inp, outp) || + ports_notify_server (inp, outp)); +} + + +static any_t +handle_auth_requests (any_t ignored) +{ + while (1) + ports_manage_port_operations_multithread (auth_bucket, auth_demuxer, + 30 * 1000, 0, 0); + return 0; +} + +int +main (int argc, char **argv) +{ + error_t err; + struct authhandle *firstauth; + auth_t authport; + pid_t child; + int status; + int argi; + + /* Parse our options. */ + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case ARGP_KEY_NO_ARGS: + argp_usage (state); + return EINVAL; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + struct argp argp = { 0, parse_opt, "COMMAND...", "\ +Run COMMAND with a fake authentication handle that claims to be root or \ +any arbitrary identity derived from that handle, but in fact is always just \ +a proxy for your real authentication handle. This means that all processes \ +created by the COMMAND will have your privileges, even though it may \ +believe it has restricted them to different identities or no identity at all.\ +" }; + + argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &argi, 0); + + auth_bucket = ports_create_bucket (); + authhandle_portclass = ports_create_class (&destroy_authhandle, 0); + + /* Create the initial root auth handle. */ + err = create_authhandle (&firstauth); + assert_perror (err); + idvec_add (&firstauth->euids, 0); + idvec_add (&firstauth->auids, 0); + idvec_add (&firstauth->auids, 0); + idvec_merge (&firstauth->egids, &firstauth->euids); + idvec_merge (&firstauth->agids, &firstauth->auids); + + /* Get a send right for it. */ + authport = ports_get_right (firstauth); + err = mach_port_insert_right (mach_task_self (), authport, authport, + MACH_MSG_TYPE_MAKE_SEND); + assert_perror (err); + ports_port_deref (firstauth); + + /* Stash our original auth port for later use. */ + real_auth_port = getauth (); + + /* Start handling auth requests on our fake handles. */ + cthread_detach (cthread_fork (&handle_auth_requests, (any_t)0)); + + /* Now we start faking ourselves out. This will immediately + reauthenticate all our descriptors through our proxy auth port. + The POSIXoid calls we make below will continue to use the fake + port and pass it on to the child. */ + if (setauth (authport)) + error (2, errno, "Cannot switch to fake auth handle"); + mach_port_deallocate (mach_task_self (), authport); + + /* We cannot use fork because it doesn't do the right thing with our send + rights that point to our own receive rights, i.e. the new auth port. + Since posix_spawn might be implemented with fork (prior to glibc 2.3), + we cannot use that simple interface either. We use _hurd_exec + directly to effect what posix_spawn does in the simple case. */ + { + task_t newtask; + process_t proc; + file_t execfile = file_name_lookup (argv[argi], O_EXEC, 0); + if (execfile == MACH_PORT_NULL) + error (3, errno, "%s", argv[argi]); + + err = task_create (mach_task_self (), +#ifdef KERN_INVALID_LEDGER + NULL, 0, /* OSF Mach */ +#endif + 0, &newtask); + if (err) + error (3, err, "cannot create child task"); + child = task2pid (newtask); + if (child < 0) + error (3, errno, "task2pid"); + proc = getproc (); + err = proc_child (proc, newtask); + mach_port_deallocate (mach_task_self (), proc); + if (err) + error (3, err, "proc_child"); + + err = _hurd_exec (newtask, execfile, &argv[argi], environ); + mach_port_deallocate (mach_task_self (), newtask); + mach_port_deallocate (mach_task_self (), execfile); + if (err) + error (3, err, "cannot execute %s", argv[argi]); + } + + if (waitpid (child, &status, 0) != child) + error (4, errno, "waitpid on %d", child); + + if (WIFSIGNALED (status)) + error (WTERMSIG (status) + 128, 0, + "%s for child %d", strsignal (WTERMSIG (status)), child); + if (WEXITSTATUS (status) != 0) + error (WEXITSTATUS (status), 0, + "Error %d for child %d", WEXITSTATUS (status), child); + + return 0; +} diff --git a/utils/fakeroot.sh b/utils/fakeroot.sh new file mode 100644 index 00000000..1ace1cf7 --- /dev/null +++ b/utils/fakeroot.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# Execute a command in an environment where it appears to be root. +# +# Copyright (C) 2002 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. +# + +USAGE="Usage: $0 [OPTION...] [COMMAND...]" +DOC="Execute COMMAND in an environment where it appears to be root." + +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_fakeroot_"; exit 0;; + --) + shift + break;; + -*) + echo 1>&2 "$0: unrecognized option \`$1'" + echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information"; + exit 1;; + *) + break;; + esac +done + +if [ $# -eq 0 ]; then + set -- ${SHELL:-/bin/sh} +fi + +# We exec settrans, which execs the "fakeauth" command in the chroot context. +# The `pwd` is evaluated here and now, and that result interpreted inside +# the shell running under fakeauth to chdir there inside the chroot world. +# That shell then execs our arguments as a command line. +exec /bin/settrans --chroot \ + /bin/fakeauth /bin/sh -c "cd `pwd`; $*" \ + -- / /hurd/fakeroot diff --git a/utils/frobauth-mod.c b/utils/frobauth-mod.c new file mode 100644 index 00000000..4464c18c --- /dev/null +++ b/utils/frobauth-mod.c @@ -0,0 +1,162 @@ +/* Modify the current authentication selected processes + + Copyright (C) 1997, 1999, 2000 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_COPY_SEND); + mach_port_deallocate (mach_task_self (), + new_auth); + 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 index 076e2a87..f3458a03 100644 --- a/utils/fsysopts.c +++ b/utils/fsysopts.c @@ -1,8 +1,8 @@ /* Set options in a running filesystem - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,2002,2004 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + 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 @@ -28,10 +28,11 @@ #include <error.h> #include <argz.h> +#include <version.h> #include <hurd/fsys.h> -char *argp_program_version = "fsysopts 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (fsysopts); static struct argp_option options[] = { @@ -40,14 +41,15 @@ static struct argp_option options[] = {0, 0, 0, 0} }; static char *args_doc = "FILESYS [FS_OPTION...]"; -static char *doc = "The legal values for FS_OPTION depends on FILESYS, but\ - some common ones are: --readonly, --writable, --remount, --sync[=INTERVAL],\ +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, --update, --sync[=INTERVAL],\ and --nosync.\n\nIf no options are supplied, FILESYS's existing options\ are printed"; /* ---------------------------------------------------------------- */ -void +int main(int argc, char *argv[]) { error_t err; @@ -58,7 +60,7 @@ main(int argc, char *argv[]) /* The filesystem options vector, in '\0' separated format. */ char *argz = 0; - int argz_len = 0; + size_t argz_len = 0; int deref = 0, recursive = 0; @@ -122,5 +124,5 @@ main(int argc, char *argv[]) puts (argz); } - exit(0); + return 0; } diff --git a/utils/ftpcp.c b/utils/ftpcp.c new file mode 100644 index 00000000..67ccb1a0 --- /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 referring 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..4ccb821d --- /dev/null +++ b/utils/ftpdir.c @@ -0,0 +1,329 @@ +/* Get a directory listing using the ftp protocol + + Copyright (C) 1997,2002 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 <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" + " occurrences 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 %6lld %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/gcore.c b/utils/gcore.c new file mode 100644 index 00000000..3d72492c --- /dev/null +++ b/utils/gcore.c @@ -0,0 +1,107 @@ +/* `gcore' program for GNU Hurd: write a core dump from a running process. + Copyright (C) 1992,2001,2002 Free Software Foundation, Inc. + Written by Roland McGrath. + +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 the GNU Hurd; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <hurd.h> +#include <hurd/crash.h> +#include <hurd/paths.h> +#include <fcntl.h> +#include <argp.h> +#include <error.h> +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (gcore); + +static const struct argp argp = +{ + NULL, NULL, + "PID...", + "Generate a core dump file from a running process" + "\vFor each PID, a core dump file \"core.PID\" is written." +}; + +int +main (int argc, char **argv) +{ + int status = 0; + file_t crashserv; + int argi; + + argp_parse (&argp, argc, argv, 0, &argi, 0); + + crashserv = file_name_lookup (_SERVERS_CRASH, 0, 0); + if (crashserv == MACH_PORT_NULL) + error (1, errno, "cannot reach crash server: %s", _SERVERS_CRASH); + + for (; argi < argc; ++argi) + { + char *end; + pid_t pid; + task_t task; + + pid = strtol (argv[argi], &end, 10); + if (end == argv[argi] || *end != '\0') + { + error (0, 0, "cannot parse process ID: %s", argv[argi]); + status = 1; + continue; + } + + task = pid2task ((pid_t) pid); + if (task == MACH_PORT_NULL) + { + error (0, errno, "pid2task: %d", pid); + status = 1; + } + else + { + file_t file; + char *name = 0; + asprintf (&name, "core.%d", pid); + + file = file_name_lookup (name, O_WRONLY|O_CREAT, + 0600 &~ getumask ()); + if (file == MACH_PORT_NULL) + { + error (0, errno, "cannot create %s", name); + status = 1; + } + else + { + error_t err = crash_dump_task (crashserv, task, file, + 0, 0, 0, 0, 0, 0, + MACH_PORT_NULL, + MACH_MSG_TYPE_COPY_SEND); + mach_port_deallocate (mach_task_self (), file); + if (err) + { + (void) remove (name); + error (0, err, "crash_dump_task on %d to %s", pid, name); + status = 1; + } + free (name); + } + } + mach_port_deallocate (mach_task_self (), task); + } + + return status; +} diff --git a/utils/hurdids.c b/utils/hurdids.c deleted file mode 100644 index 18eb3c41..00000000 --- a/utils/hurdids.c +++ /dev/null @@ -1,156 +0,0 @@ -/* Show all hurd ids - - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include <hurd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <argp.h> -#include <unistd.h> -#include <pwd.h> -#include <grp.h> - -#include <error.h> -#include <hurd/id.h> - -char *argp_program_version = "hurdids 1.0 (GNU " HURD_RELEASE ")"; - -static struct argp_option options[] = -{ - {"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"}, - {"ids", 'i', 0, 0, "Show numeric uids/gids"}, - {0} -}; -static char *args_doc = 0; -static char *doc = 0; - -/* ---------------------------------------------------------------- */ - -void -main(int argc, char *argv[]) -{ - error_t err; - int show_eff = 0, show_avail = 0, show_uids = 0, show_gids = 0; - int show_names = 0, show_ids = 0; - - /* 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 'i': show_ids = 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 (! show_eff && ! show_avail) - show_eff = show_avail = 1; - if (! show_uids && ! show_gids) - show_uids = show_gids = 1; - if (! show_names && ! show_ids) - show_names = show_ids = 1; - - mutex_lock (&_hurd_id.lock); - err = _hurd_check_ids (); - mutex_unlock (&_hurd_id.lock); - - if (err) - error (2, err, "Can't update ids"); - else - { - typedef typeof (_hurd_id.gen) *ids_t; - void print_ids (ids_t ids, char *name) - { - int i; - - if (show_uids) - { - if (name && show_gids) - printf ("%s uids: ", name); - else if (show_gids) - printf ("uids: "); - else if (name) - printf ("%s: ", name); - - for (i = 0; i < ids->nuids; i++) - { - uid_t uid = ids->uids[i]; - struct passwd *pw = show_names ? getpwuid (uid) : 0; - if (i > 0) - putchar (' '); - if (pw) - if (show_ids) - printf ("%d(%s)", uid, pw->pw_name); - else - printf ("%s", pw->pw_name); - else - printf ("%d", uid); - } - putchar ('\n'); - } - if (show_gids) - { - if (name && show_uids) - printf ("%s gids: ", name); - else if (show_uids) - printf ("gids: "); - else if (name) - printf ("%s: ", name); - - for (i = 0; i < ids->ngids; i++) - { - gid_t gid = ids->gids[i]; - struct group *gr = show_names ? getgrgid (gid) : 0; - if (i > 0) - putchar (' '); - if (gr) - if (show_ids) - printf ("%d(%s)", gid, gr->gr_name); - else - printf ("%s", gr->gr_name); - else - printf ("%d", gid); - } - putchar ('\n'); - } - } - if (show_eff) - print_ids (&_hurd_id.gen, show_avail ? "effective" : 0); - if (show_avail) - print_ids (&_hurd_id.aux, show_eff ? "available" : 0); - } - - 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 index 46f9629b..cad3b1ed 100644 --- a/utils/login.c +++ b/utils/login.c @@ -1,8 +1,8 @@ /* Hurdish login - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2002 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + 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 @@ -31,6 +31,8 @@ #include <netdb.h> #include <time.h> #include <assert.h> +#include <version.h> +#include <sys/mman.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -44,13 +46,16 @@ #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); - -char *argp_program_version = "login 1.0 (GNU " HURD_RELEASE ")"; +extern error_t +get_nonsugid_ids (struct idvec *uids, struct idvec *gids); /* Defaults for various login parameters. */ char *default_args[] = { @@ -88,26 +93,28 @@ static struct argp_option options[] = {"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"}, - {"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"}, {"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"}, - {"keep", 'k', 0, 0, "Keep the old available ids, and save the old" - "effective ids as available ids"}, + {"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."; @@ -133,7 +140,7 @@ cat (mach_port_t node, char *str) { write (0, data, data_len); if (data != buf) - vm_deallocate (mach_task_self (), (vm_address_t)data, data_len); + munmap (data, data_len); } } if (err) @@ -181,7 +188,9 @@ add_utmp_entry (char *args, unsigned args_len, int inherit_host) { 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) @@ -201,7 +210,7 @@ add_utmp_entry (char *args, unsigned args_len, int inherit_host) /* 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) +add_canonical_host (char **args, size_t *args_len, char *host) { struct hostent *he = gethostbyname (host); @@ -238,7 +247,7 @@ add_canonical_host (char **args, unsigned *args_len, char *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) +add_entry (char **env, size_t *env_len, char *entry) { char *name = strsep (&entry, "="); error_t err = envz_add (env, env_len, name, entry); @@ -263,7 +272,7 @@ check_owned (process_t proc_server, pid_t pid, int *owned) { *owned = !(pi->state & PI_NOTOWNED); if (pi != &_pi) - vm_deallocate (mach_task_self (), (vm_address_t)pi, pi_size); + munmap (pi, pi_size); } return err; @@ -288,7 +297,7 @@ kill_login (process_t proc_server, pid_t pid, int sig) if (pids[i] != self) kill (pids[i], sig); if (pids != _pids) - vm_deallocate (mach_task_self (), (vm_address_t)pids, num_pids); + munmap (pids, num_pids); } } while (!err && num_pids > 0); @@ -359,7 +368,7 @@ dog (time_t timeout, pid_t pid, char **argv) } } -void +int main(int argc, char *argv[]) { int i; @@ -368,17 +377,15 @@ main(int argc, char *argv[]) char *path; error_t err = 0; char *args = 0; /* The login parameters */ - unsigned args_len = 0; - char *passwd = 0; /* Login parameters from /etc/passwd */ - unsigned passwd_len = 0; + size_t args_len = 0; char *args_defs = 0; /* Defaults for login parameters. */ - unsigned args_defs_len = 0; + size_t args_defs_len = 0; char *env = 0; /* The new environment. */ - unsigned env_len = 0; + size_t env_len = 0; char *env_defs = 0; /* Defaults for the environment. */ - unsigned env_defs_len = 0; + size_t env_defs_len = 0; char *parent_env = 0; /* The environment we got from our parent */ - unsigned parent_env_len = 0; + size_t 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. */ @@ -387,28 +394,24 @@ main(int argc, char *argv[]) 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; + size_t 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; + size_t sh_args_len = 0; int shell_arg = 0; /* If there are shell args, use the first as the shell name. */ - struct idvec *eff_uids = make_idvec (); /* The UIDs of the new shell. */ - struct idvec *eff_gids = make_idvec (); /* The EFF_GIDs. */ - struct idvec *avail_uids = make_idvec (); /* The aux UIDs of the new shell. */ - struct idvec *avail_gids = make_idvec (); /* The aux EFF_GIDs. */ - struct idvec *parent_uids = make_idvec (); /* Parent uids, -SETUID. */ - struct idvec *parent_gids = make_idvec (); /* Parent gids, -SETGID. */ + 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 (); - mach_port_t parent_auth = getauth (); pid_t pid = getpid (), sid; /* These three functions are to do child-authenticated lookups. See @@ -437,9 +440,11 @@ main(int argc, char *argv[]) int retry_argc; char **retry_argv; char *via = envz_get (args, args_len, "VIA"); - extern void _argp_unlock_xxx (); /* Secret unknown function. */ - error (retry ? 0 : code, err, fmt, str); /* May exit... */ + 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); @@ -450,104 +455,10 @@ main(int argc, char *argv[]) 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... */ } - /* 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). */ - 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. */ - 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. */ - int parent_has_gid (gid_t gid) - { - need_parent_ids (); - return idvec_contains (parent_gids, gid); - } - /* Returns the number of parent uids. */ - int count_parent_uids () - { - need_parent_ids (); - return parent_uids->num; - } - /* Returns the number of parent gids. */ - 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) - { - extern char *crypt (const char *string, const char salt[2]); -#pragma weak crypt - char *prompt, *unencrypted, *encrypted; - - if (!password || !*password - || idvec_contains (is_group ? eff_gids : eff_uids, id) - || idvec_contains (is_group ? avail_gids : avail_uids, 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); - if (crypt) - { - encrypted = crypt (unencrypted, password); - /* Paranoia may destroya. */ - memset (unencrypted, 0, strlen (unencrypted)); - } - else - encrypted = unencrypted; - - if (name) - free (prompt); - - if (strcmp (encrypted, password) != 0) - fail (50, 0, "Incorrect password", 0); - } - /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) { @@ -581,14 +492,14 @@ main(int argc, char *argv[]) retry = 1; break; - case 'k': - need_parent_ids (); - idvec_merge (avail_uids, parent_uids); - idvec_merge (avail_gids, parent_gids); + 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); @@ -599,105 +510,32 @@ main(int argc, char *argv[]) } if (strcmp (arg, "-") == 0) - arg = 0; /* Just like there weren't any args at all. */ - /* Fall through to deal with adding the user. */ + /* An explicit no-user-specified (so remaining args can be used + to set the program args). */ + break; - case 'u': - case 'U': - case ARGP_KEY_NO_ARGS: - { - /* USER is whom to look up. If it's 0, then we hit the end of - the sh_args without seeing a user, so we want to add defaults - values for `nobody'. */ - char *user = arg ?: envz_get (args, args_len, "NOBODY"); - struct passwd *pw = - isdigit (*user) ? getpwuid (atoi (user)) : getpwnam (user); - /* True if this is the user arg and there were no user options. */ - int only_user = - (key == ARGP_KEY_ARG - && eff_uids->num == 0 && avail_uids->num <= count_parent_uids () - && eff_gids->num == 0 && avail_gids->num <= count_parent_gids ()); - - if (! pw) - if (! arg) - /* It was nobody anyway. Just use the defaults. */ - 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) - /* In paranoid mode, we don't admit we don't know about a - user, so we just ask for a password we we know the user - can't supply. */ - verify_passwd (only_user ? 0 : user, "*", -1, 0); + /* Add a bogus uid so that password verification will + fail. */ + idvec_add (&ugids.eff_uids, -1); else - fail (10, 0, "%s: Unknown user", user); - - if (arg) - /* If it's not nobody, make sure we're authorized. */ - verify_passwd (only_user ? 0 : pw->pw_name, pw->pw_passwd, - pw->pw_uid, 0); - - if (key == 'U') - /* Add available ids instead of effective ones. */ - { - idvec_add_new (avail_uids, pw->pw_uid); - idvec_add_new (avail_gids, pw->pw_gid); - } - else - { - if (key == ARGP_KEY_ARG || eff_uids->num == 0) - /* If it's the argument (as opposed to option) specifying a - user, or the first option user, then we get defaults for - various things from the password entry. */ - { - 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); - } - if (arg) /* A real user. */ - if (key == ARGP_KEY_ARG) - /* The main user arg; add both effective and available - ids (the available ids twice, for posix compatibility - -- once for the real id, and again for the saved). */ - { - /* Updates the real id in IDS to be ID. */ - void update_real (struct idvec *ids, uid_t id) - { - if (ids->num == 0 - || !idvec_tail_contains (ids, 1, ids->ids[0])) - idvec_insert (ids, 0, id); - else - ids->ids[0] = id; - } - - /* Effective */ - idvec_insert_only (eff_uids, 0, pw->pw_uid); - idvec_insert_only (eff_gids, 0, pw->pw_gid); - /* Real */ - update_real (avail_uids, pw->pw_uid); - update_real (avail_gids, pw->pw_gid); - /* Saved */ - idvec_insert_only (avail_uids, 1, pw->pw_uid); - idvec_insert_only (avail_gids, 1, pw->pw_gid); - } - else - { - idvec_add_new (eff_uids, pw->pw_uid); - idvec_add_new (eff_gids, pw->pw_gid); - } - } - } + fail (10, 0, "%s: Unknown user", arg); + } + + if (err) + fail (11, err, "%s: Can't set user!", arg); + break; - case 'g': - case 'G': - { - struct group *gr = - isdigit (*arg) ? getgrgid (atoi (arg)) : getgrnam (arg); - if (! gr) - fail (11, 0, "%s: Unknown group", arg); - verify_passwd (gr->gr_name, gr->gr_passwd, gr->gr_gid, 1); - idvec_add_new (key == 'g' ? eff_gids : avail_gids, gr->gr_gid); - } + case ARGP_KEY_INIT: + state->child_inputs[0] = &ugids_argp_params; break; default: @@ -705,7 +543,7 @@ main(int argc, char *argv[]) } return 0; } - struct argp argp = {options, parse_opt, args_doc, doc}; + 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); @@ -736,9 +574,24 @@ main(int argc, char *argv[]) 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. @@ -750,37 +603,56 @@ main(int argc, char *argv[]) d) From the user-specified defaults (--arg-default) e) From last-ditch defaults given by the DEFAULT_* defines above - The child environment is from: + 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 */ - - /* 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"); - - err = - auth_makeauth (getauth (), 0, MACH_MSG_TYPE_COPY_SEND, 0, - eff_uids->ids, eff_uids->num, - avail_uids->ids, avail_uids->num, - eff_gids->ids, eff_gids->num, - avail_gids->ids, avail_gids->num, - &auth); - if (err) - fail (3, err, "Authentication failure", 0); + { + struct passwd *pw; + char *passwd = 0; /* Login parameters from /etc/passwd */ + size_t 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 && count_parent_uids () != 0) + 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"); @@ -788,10 +660,10 @@ main(int argc, char *argv[]) setlogin (user); proc_make_login_coll (proc_server); - if (eff_uids->num + avail_uids->num == 0) + 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 + set a timer to kill this session if it hasn't acquired 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 @@ -805,8 +677,8 @@ main(int argc, char *argv[]) } } - if (eff_uids->num > 0) - proc_setowner (proc_server, eff_uids->ids[0], 0); + 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. */ @@ -821,6 +693,7 @@ main(int argc, char *argv[]) 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 (); /* " */ @@ -829,11 +702,10 @@ main(int argc, char *argv[]) if (err) error (40, err, "Port reauth failure"); - /* These are the default values for the child's root/cwd. We don't want to + /* These are the default values for the child's root. 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) @@ -888,13 +760,15 @@ main(int argc, char *argv[]) arg = envz_get (args, args_len, "HOME"); if (arg && *arg) { - cwd = child_lookup (arg, 0, O_RDONLY); + mach_port_t 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", "/"); } + else + ports[INIT_PORT_CWDIR] = cwd; } arg = envz_get (args, args_len, "ROOT"); @@ -944,25 +818,24 @@ main(int argc, char *argv[]) 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 - { - sh_arg0 = malloc (strlen (shell_base) + 2); - if (! sh_arg0) - err = ENOMEM; - else - /* Prepend the name with a `-', as is the odd custom. */ - { - sh_arg0[0] = '-'; - strcpy (sh_arg0 + 1, shell_base); - } - } + /* 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 + /* Maybe output the message of the day. Note that we use 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"); @@ -982,29 +855,28 @@ main(int argc, char *argv[]) } /* Now that we don't need to use PORTS for lookups anymore, put the correct - ROOT and CWD in. */ + ROOT 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, !parent_has_uid (0)); + add_utmp_entry (args, args_len, !idvec_contains (&parent_uids, 0)); - if ((eff_uids->num | eff_gids->num) && !no_login) + 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, - eff_uids->num ? eff_uids->ids[0] : -1, - eff_gids->num ? eff_gids->ids[0] : -1); + 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); } @@ -1019,5 +891,5 @@ main(int argc, char *argv[]) if (err) error(5, err, "%s", shell); - exit(0); + return 0; } diff --git a/utils/mount.c b/utils/mount.c new file mode 100644 index 00000000..8b059c23 --- /dev/null +++ b/utils/mount.c @@ -0,0 +1,582 @@ +/* Roughly Unix/Linux-compatible `mount' frontend for Hurd translators. + + Copyright (C) 1999, 2004 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)"}, + {"options", 'o', "OPTIONS", 0, "A `,' separated list of options"}, + {"readonly", 'r', 0, 0, "Never write to disk or allow opens for writing"}, + {"writable", 'w', 0, 0, "Use normal read/write behavior"}, + {"update", 'u', 0, 0, "Flush any meta-data cached in core"}, + {"remount", 0, 0, OPTION_ALIAS}, + {"verbose", 'v', 0, 0, "Give more detailed information"}, + {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, "update")); + 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 if (strcmp (o, "defaults") != 0) + { + /* 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, + task_t task, void *cookie) + { + 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, NULL, 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/mount.sh b/utils/mount.sh deleted file mode 100755 index a403b8d8..00000000 --- a/utils/mount.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh -# -# A simple version of mount for the hurd -# - -usage="Usage: $0: [ -rnv | -o DEVOPTS | -t TYPE | -f FSTAB ] ( DEVICE NODE | DEVICE | NODE )" - -PATH=/bin - -default_type=ufs -type="" -fstab=/etc/fstab -exec=true -echo=false - -while :; do - case $1 in - -v) echo=true; margs="$margs -v"; shift;; - -n) exec=false; margs="$margs -n"; shift;; - -r) targs="$targs -r"; shift;; - -t) case "$type" in - ""|"$2") type="$2"; shift 2; margs="$margs -t $type";; - *) echo 1>&2 $0: "$2": Filesystem type inconsistent with "$type" - exit 7;; - esac;; - -f) fstab=$2; shift 2;; - -o) targs="$targs $2"; shift 2;; - -*) echo 1>&2 $0: $1: unknown flag; echo 1>&2 "$usage"; exit 1;; - *) break;; - esac -done - -case "$targs" in ?*) - # We embed quotes so that spaces are preserved in targs later on - margs="$margs -o \"$targs\"" -esac - -case $# in - 1) - # Lookup the given single arg in /etc/fstab for the rest of the args - args=`gawk -f - $fstab <<END -\\$1 == "$1" || \\$2 == "$1" { - for (i = 4; i <= NF; i++) - printf("%s ", \\$i); - printf("-t %s %s %s", \\$3, \\$1, \\$2); - exit(0); -} -END -` - case "$args" in - "") echo 1>&2 $0: $1: not found in $fstab; exit 3;; - *) eval $0 $margs $args;; - esac - ;; - - 2) - # Do the mount, by putting an active translator on the node - - case "$type" in "") type="$default_type";; esac - - if [ ! -x /hurd/$type ]; then - echo 1>&2 $0: $type: unknown filesystem type - exit 1 - fi - - $echo && echo settrans -a $2 /hurd/$type $targs $1 - $exec && settrans -a $2 /hurd/$type $targs $1 - ;; - - *) - echo 1>&2 "$usage"; exit 1 - ;; -esac diff --git a/utils/msgport.c b/utils/msgport.c new file mode 100644 index 00000000..06b7dc37 --- /dev/null +++ b/utils/msgport.c @@ -0,0 +1,661 @@ +/* Send messages to selected processes + + Copyright (C) 1998,99,2000,02 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. */ +static inline int +str2flags (const char *str) +{ + int flags = 0; + while (*str) + { + switch (*str) + { + case 'r': flags |= O_RDONLY; break; + case 'w': flags |= O_WRONLY|O_CREAT; break; + case 'x': flags |= O_EXEC; break; + case 'a': flags |= O_APPEND; break; + default: + /* ignore */ + break; + } + ++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_COPY_SEND); + mach_port_deallocate (mach_task_self (), file); + 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 = str2flags (argc > 2 ? argv[2] : "r"); + file_t 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 = str2flags (argc > 2 ? argv[2] : "r"); + file_t file = file_name_lookup (argv[0], flags, 0666); + if (file == MACH_PORT_NULL) + return errno; + err = do_setfd (pid, msgport, STDIN_FILENO, 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 = str2flags (argc > 2 ? argv[2] : "w"); + file_t file = file_name_lookup (argv[0], flags, 0666); + if (file == MACH_PORT_NULL) + return errno; + err = do_setfd (pid, msgport, STDOUT_FILENO, 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 = str2flags (argc > 2 ? argv[2] : "w"); + file_t file = file_name_lookup (argv[0], flags, 0666); + if (file == MACH_PORT_NULL) + return errno; + err = do_setfd (pid, msgport, STDERR_FILENO, 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_COPY_SEND); + 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_COPY_SEND); + 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_COPY_SEND); + 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 [rwxa]", 0, "Change file descriptor"}, + {"stdin", CMD_STDIN, "FILE [rwxa]", 0, "Change standard input"}, + {"stdout", CMD_STDOUT, "FILE [rwxa]", 0, "Change standard output"}, + {"stderr", CMD_STDERR, "FILE [rwxa]", 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/old-ps.c b/utils/old-ps.c deleted file mode 100644 index 5dac7854..00000000 --- a/utils/old-ps.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - Copyright (C) 1994 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 <hurd.h> -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> -#include <string.h> - -/* return a string describing the amount of memory SIZE represents. */ -char * -mem_str (vm_size_t size) -{ - char *ret = malloc (10); - char *spec=" KMG"; - int dec = 0; - - while (size > 1000) - { - dec = size % 1000; - size /= 1000; - spec++; - } - - if (size >= 100) - sprintf (ret, "%d%c", size, *spec); - else if (size >= 10) - sprintf (ret, "%d.%d%c", size, dec / 100, *spec); - else - sprintf (ret, "%d.%d%c", size, dec / 10, *spec); - - return ret; -} - -/* Return a string representing time T. */ -char * -time_str (time_value_t *t) -{ - char *ret = malloc (20); - int centiseconds; - - if (t->microseconds >= 1000000) - { - t->seconds += t->microseconds / 1000000; - t->microseconds %= 1000000; - } - centiseconds = t->microseconds / (1000000 / 100); - - sprintf (ret, "%d:%02d.%02d", - t->seconds / 60, /* minutes */ - t->seconds % 60, /* seconds */ - centiseconds); - return ret; -} - -/* Print a string describing the args of proc PID */ -void -print_args_str (process_t proc, pid_t pid) -{ - char *args; - u_int nargs = 0; - char *p; - error_t err; - - err = proc_getprocargs (proc, pid, &args, &nargs); - if (err) - return; - p = args; - while (p - args < nargs) - { - printf ("%s ", p); - p = strchr (p, '\0') + 1; - } - vm_deallocate (mach_task_self (), (vm_address_t) args, nargs); -} - -/* Very simple PS */ -int -main (int argc, char **argv) -{ - process_t proc; - pid_t pids[20]; - pid_t *pp = pids; - u_int npids = 20; - int ind; - struct thread_basic_info tbi; - struct thread_sched_info tsi; - int verbose; - -#if 0 - stdout = mach_open_devstream (getdport (1), "w"); -#endif - - if (argc > 2 || - (argc == 2 && (argv[1][0] != '-' || argv[1][1] != 'v' || argv[1][2]))) - { - fprintf (stderr, "Usage: %s [-v]\n", argv[0]); - exit (1); - } - verbose = argc == 2; - - if (verbose) - puts ("PID\tUSER\tPP\tPG\tSS\tThds\tVMem\tRSS\tPRI\t%CPU\tUser\tSystem\tArgs"); - else - puts ("PID\tUSER\tThds\tVMem\tRSS\tUser\tSystem\tArgs"); - proc = getproc (); - proc_getallpids (proc, &pp, &npids); - for (ind = 0; ind < npids; ind++) - { - int procinfobuf[0]; - struct procinfo *pi = (struct procinfo *) procinfobuf; - u_int pisize = 0; - int i; - - proc_getprocinfo (proc, pp[ind], (int **)&pi, &pisize); - - if (pi->state & PI_NOPARENT) - continue; - - bzero (&tbi, sizeof tbi); - bzero (&tsi, sizeof tsi); - for (i = 0; i < pi->nthreads; i++) - { - tsi.base_priority += pi->threadinfos[i].pis_si.base_priority; - tsi.cur_priority += pi->threadinfos[i].pis_si.cur_priority; - tbi.cpu_usage += pi->threadinfos[i].pis_bi.cpu_usage; - tbi.user_time.seconds += pi->threadinfos[i].pis_bi.user_time.seconds; - tbi.user_time.microseconds - += pi->threadinfos[i].pis_bi.user_time.microseconds; - tbi.system_time.seconds - += pi->threadinfos[i].pis_bi.system_time.seconds; - tbi.system_time.microseconds - += pi->threadinfos[i].pis_bi.system_time.microseconds; - } - tsi.base_priority /= pi->nthreads; - tsi.cur_priority /= pi->nthreads; - tbi.user_time.seconds += tbi.user_time.microseconds / 1000000; - tbi.user_time.microseconds %= 1000000; - tbi.system_time.seconds += tbi.system_time.microseconds / 1000000; - tbi.system_time.microseconds %= 1000000; - if (verbose) - printf ("%d\t%d\t%d\t%d\t%d\t%d\t%s\t%s\t%d/%d\t%d\t%s\t%s\t", - pp[ind], - (pi->state & PI_NOTOWNED) ? -1 : pi->owner, - pi->ppid, - pi->pgrp, - pi->session, - pi->nthreads, - mem_str (pi->taskinfo.virtual_size), - mem_str (pi->taskinfo.resident_size), - tsi.base_priority, - tsi.cur_priority, - tbi.cpu_usage, - time_str (&tbi.user_time), - time_str (&tbi.system_time)); - else - printf ("%d\t%d\t%d\t%s\t%s\t%s\t%s\t", - pp[ind], - (pi->state & PI_NOTOWNED) ? -1 : pi->owner, - pi->nthreads, - mem_str (pi->taskinfo.virtual_size), - mem_str (pi->taskinfo.resident_size), - time_str (&tbi.user_time), - time_str (&tbi.system_time)); - print_args_str (proc, pp[ind]); - putchar ('\n'); - } - return 0; -} diff --git a/utils/old-settrans.c b/utils/old-settrans.c deleted file mode 100644 index 4d1ba167..00000000 --- a/utils/old-settrans.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Set a passive translator on a file - Copyright (C) 1994 Free Software Foundation, Inc. - Written by Michael I. Bushnell. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include <hurd.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <fcntl.h> - -int -main (int argc, char **argv) -{ - /* This is pretty kludgy for now. */ - mach_port_t file; - - char *buf, *bp; - int buflen; - int i; - - if (argc < 2) - { - fprintf (stderr, "\ -Usage: %s FILE TRANSLATOR [ARG ...]\n\ - or: %s FILE -show\n", argv[0], argv[0]); - exit (1); - } - - /* Use the O_NOTRANS flag so that we replace any existing - translator rather than piggybacking on top of it. */ - file = file_name_lookup (argv[1], O_NOTRANS, 0); - if (file == MACH_PORT_NULL) - { - perror (argv[1]); - exit (1); - } - - if (argc == 3 && !strcmp (argv[2], "-show")) - { - char buf[1024], *trans = buf; - unsigned int len = sizeof buf; - errno = file_get_translator (file, &trans, &len); - if (errno) - { - perror ("file_get_translator"); - exit (1); - } - printf ("%s translated by:", argv[1]); - do - { - char *next; - printf (" %.*s", (int) len, trans); - next = memchr (trans, '\0', len); - if (! next) - { - printf ("<unterminated>"); - break; - } - len -= ++next - trans; - trans = next; - } while (len > 0); - putchar ('\n'); - exit (0); - } - - buflen = 0; - for (i = 2; i < argc; i++) - buflen += strlen (argv[i]) + 1; - - bp = buf = alloca (buflen); - - for (i = 2; i < argc; i++) - bp = stpcpy (bp, argv[i]) + 1; - - errno = file_set_translator (file, FS_TRANS_SET, 0, 0, - buf, buflen, MACH_PORT_NULL, - MACH_MSG_TYPE_COPY_SEND); - if (errno != 0) - { - perror ("Setting translator"); - exit (1); - } - - exit (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..44cd0b44 --- /dev/null +++ b/utils/pids.c @@ -0,0 +1,231 @@ +/* Pid parsing/frobbing + + Copyright (C) 1997,99,2002 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 <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, size_t *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..dcf87e81 --- /dev/null +++ b/utils/pids.h @@ -0,0 +1,47 @@ +/* Pid parsing/frobbing + + Copyright (C) 1997,2001 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. */ + +#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/portinfo.c b/utils/portinfo.c index 10f5fe8f..e78c77df 100644 --- a/utils/portinfo.c +++ b/utils/portinfo.c @@ -1,6 +1,6 @@ /* Print information about a task's ports - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996,97,98,99, 2000 Free Software Foundation, Inc. Written by Miles Bader <miles@gnu.ai.mit.edu> @@ -25,6 +25,7 @@ #include <string.h> #include <unistd.h> #include <stdlib.h> +#include <version.h> #include <mach.h> @@ -34,15 +35,17 @@ #include <portinfo.h> #include <portxlate.h> -char *argp_program_version = "portinfo 1.0 (GNU " HURD_RELEASE ")"; +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}, @@ -54,8 +57,10 @@ static const struct argp_option options[] = { {0,0,0,0, "Translating port names between tasks:", 3}, {"translate", 't', "PID", 0, "Translate port names to process PID"}, +#if 0 {"show-targets", 'h', 0, 0, "Print a header describing the target process" }, +#endif {"no-translation-errors", 'E', 0, 0, "Don't display an error if a specified port can't be translated" }, #if 0 @@ -63,16 +68,18 @@ static const struct argp_option options[] = { {"target-receive", 'R', 0, 0, "Only show ports that translate into receive rights"}, {"target-send", 'S', 0, 0, - "Only show ports that translate into receive rights"}, + "Only show ports that translate into send rights"}, {"target-send-once",'O', 0, 0, - "Only show ports that translate into receive rights"}, + "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 = -"If no port NAMEs are given, all ports in process PID are reported (if" +"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."; @@ -96,11 +103,13 @@ parse_task (char *arg) 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 hold = 0; +static volatile int hold = 0; int main (int argc, char **argv) @@ -111,6 +120,7 @@ main (int argc, char **argv) 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... */ @@ -134,6 +144,7 @@ main (int argc, char **argv) case 't': xlate_task = parse_task (arg); break; case 'a': search = 1; break; + case 'E': no_translation_errors = 1; break; case '*': hold = 1; @@ -189,7 +200,12 @@ main (int argc, char **argv) else { if (xlator) - err = print_xlated_port_info (name, 0, xlator, show, stdout); + { + 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) @@ -1,8 +1,8 @@ /* Show process information. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2002,2006 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + 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 @@ -30,96 +30,78 @@ #include <idvec.h> #include <ps.h> #include <error.h> +#include <version.h> + #include "psout.h" +#include "parse.h" +#include "pids.h" -char *argp_program_version = "ps 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (ps); #define OA OPTION_ARG_OPTIONAL static const struct argp_option options[] = { - {"all-users", 'a', 0, 0, "List other users' processes"}, + {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, 'f', 0, 0, "Use the `full' output-format"}, {0, 'g', 0, 0, "Include session and login leaders"}, - {"no-header", 'H', 0, 0, "Don't print a descriptive header line"}, - {0, 'j', 0, 0, "Use the `jobc' output-format"}, - {0, 'l', 0, 0, "Use the `long' output-format"}, - {"login", 'L', "LID", OA, "Add the processes from the login" - " collection LID (which defaults that of" - " the current process)"}, - {"lid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN}, + {"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"}, - {"owner", 'U', "USER", 0, "Show only processes owned by USER"}, - {"not-owner", 'O', "USER", 0, "Show only processes not owned by USER"}, - {"pid", 'p', "PID", 0, "List the process PID"}, - {"pgrp", 'G', "PGRP", 0, "List processes in process group PGRP"}, - {"no-parent", 'P', 0, 0, "Include processes without parents"}, {"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"}, - {"session", 'S', "SID", OA, "Add the processes from the session SID" - " (which defaults to the sid of the" - " current process)"}, - {"sid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN}, - {"sort", 'S', "FIELD",0, "Sort the output with respect to FIELD," + {"sort", 's', "FIELD",0, "Sort the output with respect to FIELD," " backwards if FIELD is prefixed by `-'"}, - {"threads", 'T', 0, 0, "Show the threads for each process"}, - {"tty", 't', "TTY", OA, "Only show processes with controlling" - " terminal TTY"}, - {0, 'u', 0, 0, "Use the `user' output-format"}, - {0, 'v', 0, 0, "Use the `vmem' output-format"}, + {"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, 'x', 0, 0, "Include orphaned processes"}, + " remove the default limit"}, {0, 0} }; -char *args_doc = "[PID...]"; - -char *doc = "The 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 \ ---pgrp."; - -int -parse_enum(char *arg, char **choices, char *kind, int allow_mismatches) -{ - int arglen = strlen(arg); - char **p = choices; - int partial_match = -1; - - while (*p != NULL) - if (strcmp(*p, arg) == 0) - return p - choices; - else - { - if (strncmp(*p, arg, arglen) == 0) - if (partial_match >= 0) - argp_error (0, "%s: Ambiguous %s", arg, kind); - else - partial_match = p - choices; - p++; - } - - if (partial_match < 0 && !allow_mismatches) - argp_error (0, "%s: Invalid %s", arg, kind); - - return partial_match; -} +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 @@ -127,38 +109,36 @@ parse_enum(char *arg, char **choices, char *kind, int allow_mismatches) #define FILTER_UNORPHANED 0x08 #define FILTER_PARENTED 0x10 -enum procsets +/* A particular predefined output format. */ +struct output_fmt { - PROCSET_ALL, PROCSET_SESSION, PROCSET_LOGIN + const char *name; + const char *sort_key; /* How this format should be sorted. */ + const char *fmt; /* The format string. */ }; -char *procset_names[] = -{"all", "session", "login", 0}; - -/* The names of various predefined output formats. */ -char *fmt_names[] = - {"default", "user", "vmem", "long", "jobc", "full", "hurd", "hurd-long",0}; -/* How each of those formats should be sorted; */ -char *fmt_sortkeys[] = - {"pid", "-cpu","-mem", "pid", "pid", "pid", "pid", "pid"}; -/* and the actual format strings. */ -char *fmts[] = + +/* The predefined output formats. */ +struct output_fmt output_fmts[] = { - /* default */ - "%^%?user %pid %th %tt %sc %stat %time %command", - /* user (-u) */ - "%^%user %pid %th %cpu %mem %sz %rss %tt %sc %stat %command", - /* vmem (-v) */ - "%^%pid %th %stat %sl %pgins %pgflts %cowflts %zfills %sz %rss %cpu %mem %command", - /* long (-l) */ - "%^%uid %pid %th %ppid %pri %ni %nth %msgi %msgo %sz %rss %sc %wait %stat %tt %time %command", - /* jobc (-j) */ - "%^%user %pid %th %ppid %pgrp %sess %lcoll %sc %stat %tt %time %command", - /* full (-f) (from sysv) */ - "%^%-user %pid %ppid %tty %time %command", - /* hurd */ - "%pid %th %uid %nth %{vsize:Vmem} %rss %{utime:User} %{stime:System} %args", - /* hurd-long */ - "%pid %th %uid %ppid %pgrp %sess %nth %{vsize:Vmem} %rss %cpu %{utime:User} %{stime:System} %args" + { "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. */ @@ -172,141 +152,17 @@ spec_abbrevs[] = { static struct ps_fmt_specs ps_specs = { spec_abbrevs, &ps_std_fmt_specs }; -/* 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. */ -static void -_parse_strlist (char *arg, - void (*add_fn)(const char *str), void (*default_add_fn)(), - const char *type_name) -{ - if (arg) - while (isspace(*arg)) - arg++; - - if (arg == NULL || *arg == '\0') - if (default_add_fn) - (*default_add_fn)(); - else - error(7, 0, "Empty %s list", type_name); - else - { - char *end = arg; - - void mark_end() - { - *end++ = '\0'; - while (isspace(*end)) - end++; - } - void parse_element() - { - if (*arg == '\0') - error(7, 0, "Empty element in %s list", type_name); - (*add_fn)(arg); - arg = end; - } - - while (*end != '\0') - switch (*end) - { - case ' ': case '\t': - mark_end(); - if (*end == ',') - mark_end(); - parse_element(); - break; - case ',': - mark_end(); - parse_element(); - break; - default: - end++; - } - - parse_element(); - } -} - -/* 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. */ -static void -parse_strlist (char *arg, - void (*add_fn)(const char *str), - const char *(*default_fn)(), - const char *type_name) -{ - void default_str_add() { (*add_fn)((*default_fn)()); } - _parse_strlist(arg, add_fn, default_str_add, type_name); -} - -/* 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. */ -static void -parse_numlist (char *arg, - void (*add_fn)(unsigned num), - int (*default_fn)(), - int (*lookup_fn)(const char *str), - const char *type_name) -{ - void default_num_add() { (*add_fn)((*default_fn)()); } - void add_num_str(const char *str) - { - const char *p; - for (p = str; *p != '\0'; p++) - if (!isdigit(*p)) - { - if (lookup_fn) - (*add_fn)((*lookup_fn)(str)); - else - error (7, 0, "%s: Invalid %s", p, type_name); - return; - } - (*add_fn)(atoi(str)); - } - _parse_strlist(arg, add_num_str, default_fn ? default_num_add : 0, - type_name); -} - -static process_t proc_server; - -/* Returns our session id. */ -static pid_t -current_sid() -{ - pid_t sid; - error_t err = proc_getsid(proc_server, getpid(), &sid); - if (err) - error(2, err, "Couldn't get current session id"); - return sid; -} - -/* Returns our login collection id. */ -static pid_t -current_lid() -{ - pid_t lid; - error_t err = proc_getloginid(proc_server, getpid(), &lid); - if (err) - error(2, err, "Couldn't get current login collection") ; - return lid; -} - /* Returns the UID for the user called NAME. */ static int -lookup_user(const char *name) +lookup_user (const char *name, struct argp_state *state) { struct passwd *pw = getpwnam(name); if (pw == NULL) - error(2, 0, "%s: Unknown user", name); + argp_failure (state, 2, 0, "%s: Unknown user", name); return pw->pw_uid; } -void +int main(int argc, char *argv[]) { error_t err; @@ -317,10 +173,10 @@ main(int argc, char *argv[]) char *arg_hack_buf = 0; struct idvec *only_uids = make_idvec (), *not_uids = make_idvec (); char *tty_names = 0; - unsigned num_tty_names = 0; + size_t num_tty_names = 0; struct proc_stat_list *procset; struct ps_context *context; - char *fmt_string = "default", *sort_key_name = NULL; + 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; @@ -329,65 +185,26 @@ main(int argc, char *argv[]) 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 specific process to be printed out. */ - void add_pid (unsigned pid) - { - struct proc_stat *ps; - - err = proc_stat_list_add_pid (procset, pid, &ps); - if (err) - error (0, err, "%d: Can't add process", pid); - - /* See if this process actually exists. */ - proc_stat_set_flags (ps, PSTAT_PROC_INFO); - if (! proc_stat_has (ps, PSTAT_PROC_INFO)) - /* Give an error message; using ps_alive_filter below will delete the - entry so it doesn't get output. */ - error (0, 0, "%d: Unknown process", pid); - - /* If explicit processes are specified, we probably don't want to - filter them out later. This implicit turning off of filtering might - be confusing in the case where a login-collection or session is - specified along with some pids, but it's probably not worth worrying - about. */ - filter_mask = 0; - } - /* Print out all process from the given session. */ - void add_sid(unsigned sid) - { - err = proc_stat_list_add_session (procset, sid, 0, 0); - if (err) - error(2, err, "%u: Can't add session", sid); - } - /* Print out all process from the given login collection. */ - void add_lid(unsigned lid) - { - error_t err = proc_stat_list_add_login_coll (procset, lid, 0, 0); - if (err) - error(2, err, "%u: Can't add login collection", lid); - } - /* Print out all process from the given process group. */ - void add_pgrp(unsigned pgrp) - { - error_t err = proc_stat_list_add_pgrp (procset, pgrp, 0, 0); - if (err) - error(2, err, "%u: Can't add process group", pgrp); - } - /* Add a user who's processes should be printed out. */ - void add_uid (uid_t uid) + error_t add_uid (uid_t uid, struct argp_state *state) { error_t err = idvec_add (only_uids, uid); if (err) - error (23, err, "Can't add uid"); + argp_failure (state, 23, err, "Can't add uid"); + return err; } /* Add a user who's processes should not be printed out. */ - void add_not_uid (uid_t uid) + error_t add_not_uid (uid_t uid, struct argp_state *state) { error_t err = idvec_add (not_uids, uid); if (err) - error (23, err, "Can't add uid"); + 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. */ @@ -403,11 +220,12 @@ main(int argc, char *argv[]) /* Add TTY_NAME to the list for which processes with those controlling terminals will be printed. */ - void add_tty_name (const char *tty_name) + 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) - error (8, err, "%s: Can't add tty", tty_name); + argp_failure (state, 8, err, "%s: Can't add tty", tty_name); + return err; } int proc_stat_has_ctty(struct proc_stat *ps) { @@ -431,7 +249,7 @@ main(int argc, char *argv[]) } /* Returns the name of the current controlling terminal. */ - static const char *current_tty_name() + const char *current_tty_name() { error_t err; struct ps_tty *tty; @@ -463,10 +281,9 @@ main(int argc, char *argv[]) memcpy (&state->argv[state->next][1], arg, len); break; } - /* Otherwise, fall through and treat the arg as a process id. */ - case 'p': - parse_numlist(arg, add_pid, NULL, NULL, "process id"); - 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; @@ -486,6 +303,8 @@ main(int argc, char *argv[]) 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; @@ -494,22 +313,37 @@ main(int argc, char *argv[]) break; case 't': - parse_strlist (arg, add_tty_name, current_tty_name, "tty"); - break; + return parse_strlist (arg, add_tty_name, current_tty_name, "tty", state); case 'U': - parse_numlist (arg, add_uid, NULL, lookup_user, "user"); - break; + return parse_numlist (arg, add_uid, NULL, lookup_user, "user", state); case 'O': - parse_numlist (arg, add_not_uid, NULL, lookup_user, "user"); - break; - case 'S': - parse_numlist(arg, add_sid, current_sid, NULL, "session id"); - break; - case 'L': - parse_numlist(arg, add_lid, current_lid, NULL, "login collection"); + 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; - case 'G': - parse_numlist(arg, add_pgrp, NULL, NULL, "process group"); + + case ARGP_KEY_SUCCESS: + /* Select an explicit format string if FMT_STRING is a format + name. This is done here because parse_enum needs STATE. */ + { + 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, state); + 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; + } + } break; default: @@ -518,46 +352,27 @@ main(int argc, char *argv[]) return 0; } - struct argp argp = { options, parse_opt, args_doc, doc}; - - proc_server = getproc(); + 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 (proc_server, &context); + err = ps_context_create (getproc (), &context); if (err) error(1, err, "ps_context_create"); - err = proc_stat_list_create(context, &procset); - if (err) - error(1, err, "proc_stat_list_create"); - /* Parse our command line. This shouldn't ever return an error. */ argp_parse (&argp, argc, argv, 0, 0, 0); - 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); - else - filter_mask &= ~FILTER_OWNER; /* Must be an anonymous process. */ - } - - { - int fmt_index = parse_enum(fmt_string, fmt_names, "format type", 1); - if (fmt_index >= 0) - { - fmt_string = fmts[fmt_index]; - if (sort_key_name == NULL) - sort_key_name = fmt_sortkeys[fmt_index]; - } - } + err = proc_stat_list_create(context, &procset); + if (err) + error(1, err, "proc_stat_list_create"); - if (proc_stat_list_num_procs (procset) == 0) + if (num_pids == 0) + /* No explicit processes specified. */ { err = proc_stat_list_add_all (procset, 0, 0); - if (err) - error(2, err, "Can't get process list"); /* 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 @@ -576,10 +391,29 @@ main(int argc, char *argv[]) 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, @@ -606,7 +440,7 @@ main(int argc, char *argv[]) psout (procset, fmt_string, posix_fmt, &ps_specs, sort_key_name, sort_reverse, output_width, print_heading, - squash_bogus_fields, squash_nominal_fields); + squash_bogus_fields, squash_nominal_fields, top); - exit (0); + return 0; } diff --git a/utils/psout.c b/utils/psout.c index 7f2b0591..93667b28 100644 --- a/utils/psout.c +++ b/utils/psout.c @@ -1,6 +1,6 @@ /* Common output function for ps & w - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc. Written by Miles Bader <miles@gnu.ai.mit.edu> @@ -31,7 +31,8 @@ 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 squash_bogus_fields, int squash_nominal_fields, + int top) { error_t err; struct ps_stream *output; @@ -96,15 +97,17 @@ psout (struct proc_stat_list *procs, error (5, err, "Can't make output stream"); if (print_heading) - if (proc_stat_list_num_procs (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 (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 (1, 0, "No applicable processes"); + } if (output_width) /* Try and restrict the number of output columns. */ @@ -117,6 +120,23 @@ psout (struct proc_stat_list *procs, 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) diff --git a/utils/psout.h b/utils/psout.h index f9c44484..f4469ac4 100644 --- a/utils/psout.h +++ b/utils/psout.h @@ -1,6 +1,6 @@ /* Common output function for ps & w - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. Written by Miles Bader <miles@gnu.ai.mit.edu> @@ -23,10 +23,12 @@ #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, +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 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..996d4bae --- /dev/null +++ b/utils/rpctrace.c @@ -0,0 +1,1238 @@ +/* Trace RPCs sent to selected ports + + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2005, 2006, 2009, 2011 + 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., 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 <fnmatch.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <argp.h> +#include <error.h> +#include <string.h> +#include <version.h> +#include <sys/wait.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <argz.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace); + +#define STD_MSGIDS_DIR DATADIR "/msgids/" + +static unsigned strsize = 80; + +#define OPT_NOSTDINC -1 +static const struct argp_option options[] = +{ + {"output", 'o', "FILE", 0, "Send trace output to FILE instead of stderr."}, + {"nostdinc", OPT_NOSTDINC, 0, 0, + "Do not search inside the standard system directory, `" STD_MSGIDS_DIR + "', for `.msgids' files."}, + {"rpc-list", 'i', "FILE", 0, + "Read FILE for assocations of message ID numbers to names."}, + {0, 'I', "DIR", 0, + "Add the directory DIR to the list of directories to be searched for files " + "containing message ID numbers."}, + {0, 's', "SIZE", 0, "Specify the maximum string size to print (the default is 80)."}, + {0} +}; + +static const char args_doc[] = "COMMAND [ARG...]"; +static const char doc[] = "Trace Mach Remote Procedure Calls."; + +/* The msgid_ihash table maps msgh_id values to names. */ + +struct msgid_info +{ + char *name; + char *subsystem; +}; + +static void +msgid_ihash_cleanup (void *element, void *arg) +{ + struct msgid_info *info = element; + free (info->name); + free (info->subsystem); + free (info); +} + +static struct hurd_ihash msgid_ihash + = HURD_IHASH_INITIALIZER (HURD_IHASH_NO_LOCP); + +/* Parse a file of RPC names and message IDs as output by mig's -list + option: "subsystem base-id routine n request-id reply-id". Put each + request-id value into `msgid_ihash' with the routine name as its value. */ +static void +parse_msgid_list (const char *filename) +{ + FILE *fp; + char *buffer = NULL; + size_t bufsize = 0; + unsigned int lineno = 0; + char *name, *subsystem; + unsigned int msgid; + error_t err; + + fp = fopen (filename, "r"); + if (fp == 0) + { + error (2, errno, "%s", filename); + return; + } + + while (getline (&buffer, &bufsize, fp) > 0) + { + ++lineno; + if (buffer[0] == '#' || buffer[0] == '\0') + continue; + if (sscanf (buffer, "%as %*u %as %*u %u %*u\n", + &subsystem, &name, &msgid) != 3) + error (0, 0, "%s:%u: invalid format in RPC list file", + filename, lineno); + else + { + struct msgid_info *info = malloc (sizeof *info); + if (info == 0) + error (1, errno, "malloc"); + info->name = name; + info->subsystem = subsystem; + err = hurd_ihash_add (&msgid_ihash, msgid, info); + if (err) + error (1, err, "hurd_ihash_add"); + } + } + + free (buffer); + fclose (fp); +} + +/* Look for a name describing MSGID. We check the table directly, and + also check if this looks like the ID of a reply message whose request + ID is already in the table. */ +static const struct msgid_info * +msgid_info (mach_msg_id_t msgid) +{ + const struct msgid_info *info = hurd_ihash_find (&msgid_ihash, msgid); + if (info == 0 && (msgid / 100) % 2 == 1) + { + /* This message ID is not in the table, and its number makes it + what should be an RPC reply message ID. So look up the message + ID of the corresponding RPC request and synthesize a name from + that. Then stash that name in the table so the next time the + lookup will match directly. */ + info = hurd_ihash_find (&msgid_ihash, msgid - 100); + if (info != 0) + { + struct msgid_info *reply_info = malloc (sizeof *info); + if (reply_info != 0) + { + reply_info->subsystem = strdup (info->subsystem); + reply_info->name = 0; + asprintf (&reply_info->name, "%s-reply", info->name); + hurd_ihash_add (&msgid_ihash, msgid, reply_info); + info = reply_info; + } + else + info = 0; + } + } + return info; +} + +static const char * +msgid_name (mach_msg_id_t msgid) +{ + const struct msgid_info *info = msgid_info (msgid); + return info ? info->name : 0; +} + +/* Return true if this message's data should be printed out. + For a request message, that means the in parameters. + For a reply messages, that means the return code and out parameters. */ +static int +msgid_display (const struct msgid_info *info) +{ + return 1; +} + +/* Return true if we should interpose on this RPC's reply port. If this + returns false, we will pass the caller's original reply port through so + we never see the reply message at all. */ +static int +msgid_trace_replies (const struct msgid_info *info) +{ + return 1; +} + +/* We keep one of these structures for each port right we are tracing. */ +struct traced_info +{ + struct port_info pi; + + mach_port_t forward; /* real port */ + mach_msg_type_name_t type; + + char *name; /* null or a string describing this */ + + union + { + struct traced_info *nextfree; /* Link when on free list. */ + + struct /* For a send right wrapper. */ + { + hurd_ihash_locp_t locp; /* position in the traced_names hash table */ + } send; + + struct /* For a send-once right wrapper. */ + { + /* We keep track of the send right to which the message containing + this send-once right as its reply port was sent, and the msgid of + that request. We don't hold a reference to the send right; it is + just a hint to indicate a match with a send right on which we just + forwarded a message. */ + mach_port_t sent_to; + mach_msg_id_t sent_msgid; + } send_once; + } u; +}; +#define INFO_SEND_ONCE(info) ((info)->type == MACH_MSG_TYPE_MOVE_SEND_ONCE) + +static struct traced_info *freelist; + +struct hurd_ihash traced_names + = HURD_IHASH_INITIALIZER (offsetof (struct traced_info, u.send.locp)); +struct port_class *traced_class; +struct port_bucket *traced_bucket; +FILE *ostream; + +/* These are the calls made from the tracing engine into + the output formatting code. */ + +/* Called for a message that does not look like an RPC reply. + The header has already been swapped into the sender's view + with interposed ports. */ +static void print_request_header (struct traced_info *info, + mach_msg_header_t *header); + +/* Called for a message that looks like an RPC reply. */ +static void print_reply_header (struct traced_info *info, + mig_reply_header_t *header); + +/* Called for each data item (which might be an array). + Always called after one of the above two. */ +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); + +/*** Mechanics of tracing messages and interposing on ports ***/ + + +/* Create a new wrapper port and do `ports_get_right' on it. */ +static struct traced_info * +new_send_wrapper (mach_port_t right, mach_port_t *wrapper_right) +{ + error_t err; + struct traced_info *info; + + /* 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->name = 0; + } + + info->forward = right; + info->type = MACH_MSG_TYPE_MOVE_SEND; + + /* 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 = hurd_ihash_add (&traced_names, info->forward, info); + assert_perror (err); + ports_port_ref_weak (info); + assert (info->u.send.locp != 0); + + *wrapper_right = ports_get_right (info); + ports_port_deref (info); + + return info; +} + +/* Create a new wrapper port and do `ports_get_right' on it. */ +static struct traced_info * +new_send_once_wrapper (mach_port_t right, mach_port_t *wrapper_right) +{ + error_t err; + struct traced_info *info; + + /* 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->name = 0; + } + + info->forward = right; + info->type = MACH_MSG_TYPE_MOVE_SEND_ONCE; + + /* Send-once rights 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. */ + + *wrapper_right = info->pi.port_right; + memset (&info->u.send_once, 0, sizeof info->u.send_once); + + return info; +} + + +/* 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->type == MACH_MSG_TYPE_MOVE_SEND); + assert (info->u.send.locp); + + /* Remove INFO from the hash table. */ + hurd_ihash_locp_remove (&traced_names, info->u.send.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); + + free (info->name); + info->name = 0; + + info->u.nextfree = freelist; + freelist = info; +} + + +/* Rewrite a port right in a message with an appropriate wrapper port. */ +static struct traced_info * +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 = hurd_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 info; + } + + /* 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 info; + } + + /* We have never seen this port before. Create a new wrapper port + and replace the right in the message with a right to it. */ + *type = MACH_MSG_TYPE_MAKE_SEND; + return new_send_wrapper (*right, right); + + 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; + return new_send_once_wrapper (*right, right); + + 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 */ + char *name; + + info = hurd_ihash_find (&traced_names, *right); + if (info) + { + /* This is a receive right that we have been tracing sends to. */ + name = info->name; + 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 (); + name = 0; + } + + /* 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); + info->name = name; + info->type = MACH_MSG_TYPE_MOVE_SEND; /* XXX ? */ + + /* 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 = hurd_ihash_add (&traced_names, info->forward, info); + 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 info; + } + + default: + assert (!"??? bogus port type from kernel!"); + } + return 0; +} + +static void +print_contents (mach_msg_header_t *inp, + void *msg_buf_ptr) +{ + error_t err; + + int first = 1; + + /* 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)); + + if (first) + first = 0; + else + putc (' ', ostream); + + /* 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; + struct traced_info *ti; + + assert (inp->msgh_bits & MACH_MSGH_BITS_COMPLEX); + assert (eltsize == sizeof (mach_port_t)); + + poly = 0; + for (i = 0; i < nelt; ++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; + } + + ti = rewrite_right (&portnames[i], &newtypes[i]); + + putc ((i == 0 && nelt > 1) ? '{' : ' ', ostream); + + if (portnames[i] == MACH_PORT_NULL) + fprintf (ostream, "(null)"); + else if (portnames[i] == MACH_PORT_DEAD) + fprintf (ostream, "(dead)"); + else + { + assert (ti); + if (ti->name != 0) + fprintf (ostream, "%s", ti->name); + else + fprintf (ostream, "%3u", (unsigned int) portnames[i]); + } + if (i > 0 && newtypes[i] != newtypes[0]) + poly = 1; + } + if (nelt > 1) + putc ('}', ostream); + + 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; + } + if (type->msgt_longform) + lt->msgtl_name = name; + else + type->msgt_name = name; + } + else if (nelt > 0 && newtypes[0] != name) + { + if (type->msgt_longform) + lt->msgtl_name = newtypes[0]; + else + type->msgt_name = newtypes[0]; + } + } + else + print_data (name, data, nelt, eltsize); + } +} + +int +trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + const mach_msg_type_t RetCodeType = + { + MACH_MSG_TYPE_INTEGER_32, /* msgt_name = */ + 32, /* msgt_size = */ + 1, /* msgt_number = */ + TRUE, /* msgt_inline = */ + FALSE, /* msgt_longform = */ + FALSE, /* msgt_deallocate = */ + 0 /* msgt_unused = */ + }; + + error_t err; + const struct msgid_info *msgid; + struct traced_info *info; + 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); + + /* A notification message from the kernel appears to have been sent + with a send-once right, even if there have never really been any. */ + 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; + } + } + + assert (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == info->type); + + complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX; + + msgid = msgid_info (inp->msgh_id); + + /* 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 && msgid_trace_replies (msgid)) + { + struct traced_info *info; + info = rewrite_right (&inp->msgh_local_port, &reply_type); + assert (info); + if (info->name == 0) + { + if (msgid == 0) + asprintf (&info->name, "reply(%u:%u)", + (unsigned int) info->pi.port_right, + (unsigned int) inp->msgh_id); + else + asprintf (&info->name, "reply(%u:%s)", + (unsigned int) info->pi.port_right, msgid->name); + } + if (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE) + { + info->u.send_once.sent_to = info->pi.port_right; + info->u.send_once.sent_msgid = inp->msgh_id; + } + } + + 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. */ + free (info->name); + info->name = 0; + 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); + } + + /* The message now appears as it would if we were the sender. + It is ready to be resent. */ + + if (msgid_display (msgid)) + { + if (inp->msgh_local_port == MACH_PORT_NULL + && info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE + && inp->msgh_size >= sizeof (mig_reply_header_t) + && (*(int *) &((mig_reply_header_t *) inp)->RetCodeType + == *(int *)&RetCodeType)) + { + /* This sure looks like an RPC reply message. */ + mig_reply_header_t *rh = (void *) inp; + print_reply_header (info, rh); + putc (' ', ostream); + print_contents (&rh->Head, rh + 1); + putc ('\n', ostream); + } + else + { + /* Print something about the message header. */ + print_request_header (info, inp); + print_contents (inp, inp + 1); + if (inp->msgh_local_port == MACH_PORT_NULL) /* simpleroutine */ + fprintf (ostream, ");\n"); + else + /* Leave a partial line that will be finished later. */ + fprintf (ostream, ")"); + } + } + + /* 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; +} + +/*** Output formatting ***/ + +#if 0 +struct msg_type +{ + const char *name; + const char *letter; +}; + +static const char *const msg_types[] = +{ + [MACH_MSG_TYPE_BIT] = {"bool", "b"}, + [MACH_MSG_TYPE_INTEGER_16] = {"int16", "h"}, + [MACH_MSG_TYPE_INTEGER_32] = {"int32", "i"}, + [MACH_MSG_TYPE_CHAR] = {"char", "c"}, + [MACH_MSG_TYPE_INTEGER_8] = {"int8", "B"}, + [MACH_MSG_TYPE_REAL] = {"float", "f"}, + [MACH_MSG_TYPE_INTEGER_64] = {"int64", "q"}, + [MACH_MSG_TYPE_STRING] = {"string", "s"}, + [MACH_MSG_TYPE_MOVE_RECEIVE] = {"move-receive", "R"}, + [MACH_MSG_TYPE_MOVE_SEND] = {"move-send", "S"}, + [MACH_MSG_TYPE_MOVE_SEND_ONCE]= {"move-send-once", "O"}, + [MACH_MSG_TYPE_COPY_SEND] = {"copy-send", "s"}, + [MACH_MSG_TYPE_MAKE_SEND] = {"make-send", ""}, + [MACH_MSG_TYPE_MAKE_SEND_ONCE]= {"make-send-once", ""}, + [MACH_MSG_TYPE_PORT_NAME] = {"port-name", "n"}, +}; +#endif + +static mach_port_t expected_reply_port; + +static void +print_request_header (struct traced_info *receiver, mach_msg_header_t *msg) +{ + const char *msgname = msgid_name (msg->msgh_id); + + expected_reply_port = msg->msgh_local_port; + + if (receiver->name != 0) + fprintf (ostream, "%4s->", receiver->name); + else + fprintf (ostream, "%4u->", (unsigned int) receiver->pi.port_right); + + if (msgname != 0) + fprintf (ostream, "%5s (", msgname); + else + fprintf (ostream, "%5u (", (unsigned int) msg->msgh_id); +} + +static void +unfinished_line (void) +{ + /* A partial line was printed by print_request_header, but + cannot be finished before we print something else. + Finish this line with the name of the reply port that + will appear in the disconnected reply later on. */ + fprintf (ostream, " > %4u ...\n", expected_reply_port); +} + +static void +print_reply_header (struct traced_info *info, mig_reply_header_t *reply) +{ + if (info->pi.port_right == expected_reply_port) + { + /* We have printed a partial line for the request message, + and now we have the corresponding reply. */ + if (reply->Head.msgh_id == info->u.send_once.sent_msgid + 100) + fprintf (ostream, " = "); /* normal case */ + else + /* This is not the proper reply message ID. */ + fprintf (ostream, " =(%u != %u) ", + reply->Head.msgh_id, + info->u.send_once.sent_msgid + 100); + } + else + { + /* This does not match up with the last thing printed. */ + if (expected_reply_port != MACH_PORT_NULL) + /* We don't print anything if the last call was a simpleroutine. */ + unfinished_line (); + if (info->name == 0) + /* This was not a reply port in previous message sent + through our wrappers. */ + fprintf (ostream, "reply?%4u", + (unsigned int) info->pi.port_right); + else + fprintf (ostream, "%s%4u", + info->name, (unsigned int) info->pi.port_right); + if (reply->Head.msgh_id == info->u.send_once.sent_msgid + 100) + /* This is a normal reply to a previous request. */ + fprintf (ostream, " > "); + else + { + /* Weirdo. */ + const char *msgname = msgid_name (reply->Head.msgh_id); + if (msgname == 0) + fprintf (ostream, " >(%u) ", reply->Head.msgh_id); + else + fprintf (ostream, " >(%s) ", msgname); + } + } + + if (reply->RetCode == 0) + fprintf (ostream, "0"); + else + { + const char *str = strerror (reply->RetCode); + if (str == 0) + fprintf (ostream, "%#x", reply->RetCode); + else + fprintf (ostream, "%#x (%s)", reply->RetCode, str); + } + + expected_reply_port = MACH_PORT_NULL; +} + + +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_PORT_NAME: + assert (eltsize == sizeof (mach_port_t)); + { + mach_msg_type_number_t i; + fprintf (ostream, "pn{"); + for (i = 0; i < nelt; ++i) + { + fprintf (ostream, "%*u", (i > 0) ? 4 : 3, + (unsigned int) ((mach_port_t *) data)[i]); + } + fprintf (ostream, "}"); + return; + } + + case MACH_MSG_TYPE_STRING: + case MACH_MSG_TYPE_CHAR: + if (nelt > strsize) + nelt = strsize; + fprintf (ostream, "\"%.*s\"", + (int) (nelt * eltsize), (const char *) data); + return; + +#if 0 + case MACH_MSG_TYPE_CHAR: + if (eltsize == 1) + FMT ("'%c'", unsigned char); + break; +#endif + +#define FMT(fmt, ctype) do { \ + mach_msg_type_number_t i; \ + for (i = 0; i < nelt; ++i) \ + { \ + fprintf (ostream, "%s" fmt, \ + (i == 0 && nelt > 1) ? "{" : i > 0 ? " " : "", \ + *(const ctype *) data); \ + data += eltsize; \ + } \ + if (nelt > 1) \ + putc ('}', ostream); \ + return; \ + } while (0) + + 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: + switch (eltsize) + { + case 1: FMT ("%"PRId8, int8_t); + case 2: FMT ("%"PRId16, int16_t); + case 4: FMT ("%"PRId32, int32_t); + case 8: FMT ("%"PRId64, int64_t); + } + break; + + case MACH_MSG_TYPE_REAL: + if (eltsize == sizeof (float)) + FMT ("%g", float); + else if (eltsize == sizeof (double)) + FMT ("%g", double); + else if (eltsize == sizeof (long double)) + FMT ("%Lg", long double); + else + abort (); + break; + } + + /* XXX */ + fprintf (ostream, "\t%#x (type %d, %d*%d)\n", *(const int *)data, type, + nelt, eltsize); +} + + +/*** Main program and child startup ***/ + +task_t traced_task; + + +/* 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; + struct traced_info *ti; + 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 (), +#ifdef KERN_INVALID_LEDGER + NULL, 0, /* OSF Mach */ +#endif + 0, &traced_task); + 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"); + + /* Create a trace wrapper for the task port. */ + ti = new_send_wrapper (traced_task, &task_wrapper);/* consumes ref */ + asprintf (&ti->name, "task%d", (int) pid); + + /* 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); + + /* 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; +} + + +static void +scan_msgids_dir (char **argz, size_t *argz_len, char *dir, bool append) +{ + struct dirent **eps; + int n; + + int + msgids_file_p (const struct dirent *eps) + { + if (fnmatch ("*.msgids", eps->d_name, 0) != FNM_NOMATCH) + return 1; + return 0; + } + + n = scandir (dir, &eps, msgids_file_p, NULL); + if (n >= 0) + { + for (int cnt = 0; cnt < n; ++cnt) + { + char *msgids_file; + + if (asprintf (&msgids_file, "%s/%s", dir, eps[cnt]->d_name) < 0) + error (1, errno, "asprintf"); + + if (append == TRUE) + { + if (argz_add (argz, argz_len, msgids_file) != 0) + error (1, errno, "argz_add"); + } + else + { + if (argz_insert (argz, argz_len, *argz, msgids_file) != 0) + error (1, errno, "argz_insert"); + } + free (msgids_file); + } + } + + /* If the directory couldn't be scanned for whatever reason, just ignore + it. */ +} + +int +main (int argc, char **argv, char **envp) +{ + char *msgids_files_argz = NULL; + size_t msgids_files_argz_len = 0; + bool nostdinc = FALSE; + const char *outfile = 0; + char **cmd_argv = 0; + + /* Parse our options... */ + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case 'o': + outfile = arg; + break; + + case OPT_NOSTDINC: + nostdinc = TRUE; + break; + + case 'i': + if (argz_add (&msgids_files_argz, &msgids_files_argz_len, + arg) != 0) + error (1, errno, "argz_add"); + break; + + case 'I': + scan_msgids_dir (&msgids_files_argz, &msgids_files_argz_len, + arg, TRUE); + break; + + case 's': + strsize = atoi (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); + + /* Insert the files from STD_MSGIDS_DIR at the beginning of the list, so that + their content can be overridden by subsequently parsed files. */ + if (nostdinc == FALSE) + scan_msgids_dir (&msgids_files_argz, &msgids_files_argz_len, + STD_MSGIDS_DIR, FALSE); + + if (msgids_files_argz != NULL) + { + char *msgids_file = NULL; + + while ((msgids_file = argz_next (msgids_files_argz, + msgids_files_argz_len, msgids_file))) + parse_msgid_list (msgids_file); + + free (msgids_files_argz); + } + + if (outfile) + { + ostream = fopen (outfile, "w"); + if (!ostream) + error (1, errno, "%s", outfile); + } + else + ostream = stderr; + setlinebuf (ostream); + + traced_bucket = ports_create_bucket (); + traced_class = ports_create_class (0, &traced_dropweak); + + hurd_ihash_set_cleanup (&msgid_ihash, msgid_ihash_cleanup, 0); + + /* 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; +} 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 index ff264a5b..a8814016 100644 --- a/utils/settrans.c +++ b/utils/settrans.c @@ -1,8 +1,7 @@ /* Set a file's translator. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> + Copyright (C) 1995,96,97,98,2001,02 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 @@ -23,14 +22,21 @@ #include <stdlib.h> #include <string.h> #include <argp.h> +#include <error.h> #include <fcntl.h> #include <unistd.h> #include <error.h> #include <argz.h> #include <hurd/fshelp.h> +#include <hurd/process.h> +#include <version.h> + +#include <hurd/lookup.h> +#include <hurd/fsys.h> + -char *argp_program_version = "settrans 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (settrans); #define DEFAULT_TIMEOUT 60 @@ -39,33 +45,41 @@ char *argp_program_version = "settrans 1.0 (GNU " HURD_RELEASE ")"; static struct argp_option options[] = { - {"active", 'a', 0, 0, "Set NODE's active translator", 1}, - {"passive", 'p', 0, 0, "Set NODE's passive translator"}, - {"create", 'c', 0, 0, "Create NODE if it doesn't exist"}, + {"active", 'a', 0, 0, "Start TRANSLATOR and set it as NODE's active translator" }, + {"passive", 'p', 0, 0, "Change NODE's passive translator record (default)" }, + {"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"}, + {"exclusive", 'x', 0, 0, "Only set the translator if there is not one already"}, + {"orphan", 'o', 0, 0, "Disconnect old translator from the filesystem " + "(do not ask it to go away)"}, + + {"chroot", 'C', 0, 0, + "Instead of setting the node's translator, take following arguments up to" + " `--' and run that command chroot'd to the translated node."}, {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"}, + {"goaway", 'g', 0, 0, "Ask the active translator to go away"}, + {"keep-active", 'k', 0, 0, "Leave any existing active translator running"}, - {0,0,0,0, "When an active translator is told to go away:", 2}, + {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"}, + {"force", 'f', 0, 0, "Ask it to ignore current users and shutdown " + "anyway." }, {"nosync", 'S', 0, 0, "Don't sync it before killing it"}, {0, 0} }; static char *args_doc = "NODE [TRANSLATOR ARG...]"; -static char *doc = "By default the passive translator is set."; +static char *doc = "Set the passive/active translator on NODE." +"\vBy default the passive translator is set."; /* ---------------------------------------------------------------- */ -void +int main(int argc, char *argv[]) { error_t err; @@ -76,7 +90,7 @@ main(int argc, char *argv[]) /* The translator's arg vector, in '\0' separated format. */ char *argz = 0; - int argz_len = 0; + size_t argz_len = 0; /* The control port for any active translator we start up. */ fsys_t active_control = MACH_PORT_NULL; @@ -88,9 +102,11 @@ main(int argc, char *argv[]) int goaway_flags = 0; /* Various option flags. */ - int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0; + int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0, + orphan = 0; int excl = 0; int timeout = DEFAULT_TIMEOUT * 1000; /* ms */ + char **chroot_command = 0; /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) @@ -120,6 +136,32 @@ main(int argc, char *argv[]) case 'g': kill_active = 1; break; case 'x': excl = 1; break; case 'P': pause = 1; break; + case 'o': orphan = 1; break; + + case 'C': + if (chroot_command) + { + argp_error (state, "--chroot given twice"); + return EINVAL; + } + chroot_command = &state->argv[state->next]; + while (state->next < state->argc) + { + if (!strcmp (state->argv[state->next], "--")) + { + state->argv[state->next++] = 0; + if (chroot_command[0] == 0) + { + argp_error (state, + "--chroot must be followed by a command"); + return EINVAL; + } + return 0; + } + ++state->next; + } + argp_error (state, "--chroot command must be terminated with `--'"); + return EINVAL; case 'c': lookup_flags |= O_CREAT; break; case 'L': lookup_flags &= ~O_NOTRANS; break; @@ -140,49 +182,63 @@ main(int argc, char *argv[]) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); - if (!active && !passive) + if (!active && !passive && !chroot_command) 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); + active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0) + | (orphan ? FS_TRANS_ORPHAN : 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) { + /* 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 || chroot_command) && 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) + mach_msg_type_name_t *underlying_type, + task_t task, void *cookie) { if (pause) { - fprintf (stderr, "Pausing..."); + fprintf (stderr, "Translator pid: %d\nPausing...", + task2pid (task)); getchar (); } node = file_name_lookup (node_name, flags | lookup_flags, 0666); if (node == MACH_PORT_NULL) - return errno; + { + 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); + err = fshelp_start_translator (open_node, NULL, argz, argz, argz_len, + timeout, &active_control); if (err) - error(4, err, "%s", argz); + /* 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 { @@ -191,13 +247,46 @@ main(int argc, char *argv[]) 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); + if (active || passive) + { + 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); + } + + if (chroot_command) + { + /* We will act as the parent filesystem would for a lookup + of the active translator's root node, then use this port + as our root directory while we exec the command. */ + + char retry_name[1024]; /* XXX */ + retry_type do_retry; + mach_port_t root; + err = fsys_getroot (active_control, + MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND, + NULL, 0, NULL, 0, 0, &do_retry, retry_name, &root); + mach_port_deallocate (mach_task_self (), active_control); + if (err) + error (6, err, "fsys_getroot"); + err = hurd_file_name_lookup_retry (&_hurd_ports_use, &getdport, 0, + do_retry, retry_name, 0, 0, + &root); + if (err) + error (6, err, "cannot resolve root port"); + + if (setcrdir (root)) + error (7, errno, "cannot install root port"); + mach_port_deallocate (mach_task_self (), root); + if (chdir ("/")) + error (8, errno, "cannot chdir to new root"); + + execvp (chroot_command[0], chroot_command); + error (8, errno, "cannot execute %s", chroot_command[0]); + } - exit(0); + return 0; } diff --git a/utils/shd.c b/utils/shd.c index cb4b0ad0..a1a4b26b 100644 --- a/utils/shd.c +++ b/utils/shd.c @@ -1,5 +1,5 @@ -/* - Copyright (C) 1994, 1995 Free Software Foundation +/* + Copyright (C) 1994,95,99,2002 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 @@ -23,6 +23,7 @@ #include <device/device.h> #include <unistd.h> #include <errno.h> +#include <error.h> #include <sys/wait.h> #include <fcntl.h> #include <stdlib.h> @@ -44,7 +45,7 @@ reap (pid_t waitfor) if (pid == -1) { if (errno != ECHILD && errno != EWOULDBLOCK) - perror ("waitpid"); + error (0, errno, "waitpid"); return; } else if (WIFEXITED (status)) @@ -54,7 +55,7 @@ reap (pid_t waitfor) printf ("PID %d %s\n", pid, strsignal (WTERMSIG (status))); else if (WIFSTOPPED (status)) - printf ("PID %d stopped: %s\n", + printf ("PID %d stopped: %s\n", pid, strsignal (WSTOPSIG (status))); else printf ("PID %d bizarre status %#x\n", pid, status); @@ -69,6 +70,7 @@ run (char **argv, int fd0, int fd1) { file_t file; char *program; + error_t err; if (strchr (argv[0], '/') != NULL) program = argv[0]; @@ -84,18 +86,22 @@ run (char **argv, int fd0, int fd1) file = file_name_lookup (program, O_EXEC, 0); if (file == MACH_PORT_NULL) { - perror (program); + error (0, errno, "%s", program); return -1; } else { task_t task; pid_t pid; - - errno = task_create (mach_task_self (), 0, &task); - if (errno) + + err = task_create (mach_task_self (), +#ifdef KERN_INVALID_LEDGER + NULL, 0, /* OSF Mach */ +#endif + 0, &task); + if (err) { - perror ("task_create"); + error (0, err, "task_create"); pid = -1; } else @@ -109,12 +115,12 @@ run (char **argv, int fd0, int fd1) *save = dup (to); if (*save < 0) { - perror ("dup"); + error (0, errno, "dup"); return -1; } if (dup2 (from, to) != to) { - perror ("dup2"); + error (0, errno, "dup2"); return -1; } close (from); @@ -126,7 +132,7 @@ run (char **argv, int fd0, int fd1) return 0; if (dup2 (*save, to) != to) { - perror ("dup2"); + error (0, errno, "dup2"); return -1; } close (*save); @@ -135,13 +141,16 @@ run (char **argv, int fd0, int fd1) pid = task2pid (task); if (pid == -1) - perror ("task2pid"), pid = 0; - errno = proc_child (proc, task); - if (errno) - perror ("proc_child"); + { + error (0, errno, "task2pid"); + pid = 0; + } + err = proc_child (proc, task); + if (err) + error (0, err, "proc_child"); if (pause_startup) { - printf ("Pausing..."); + printf ("Pausing (child PID %d)...", pid); fflush (stdout); getchar (); } @@ -149,25 +158,26 @@ run (char **argv, int fd0, int fd1) if (movefd (fd0, 0, &save0) || movefd (fd1, 1, &save1)) return -1; - - errno = _hurd_exec (task, file, argv, environ); + + err = _hurd_exec (task, file, argv, environ); if (restorefd (fd0, 0, &save0) || restorefd (fd1, 1, &save1)) return -1; - if (errno) + if (err) { - perror ("_hurd_exec"); - errno = task_terminate (task); - if (errno) - perror ("task_terminate"); + error (0, err, "_hurd_exec"); + err = task_terminate (task); + if (err) + error (0, err, "task_terminate"); } mach_port_deallocate (mach_task_self (), task); } mach_port_deallocate (mach_task_self (), file); + errno = err; return pid; } } @@ -183,7 +193,7 @@ command (int argc, char **argv) bg = !strcmp (argv[argc - 1], "&"); if (bg) argv[--argc] = NULL; - + start = 0; for (i = 1; i < argc; ++i) if (! strcmp (argv[i], "|")) @@ -192,7 +202,7 @@ command (int argc, char **argv) argv[i] = NULL; if (pipe (fds)) { - perror ("pipe"); + error (0, errno, "pipe"); return; } pid = run (argv + start, fd0, fds[1]); @@ -213,7 +223,7 @@ command (int argc, char **argv) int -main () +main (int argc, char *argv[]) { char *linebuf = NULL; size_t linebufsize = 0; @@ -228,15 +238,15 @@ main () 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); @@ -266,7 +276,7 @@ main () { if (feof (stdin)) return 0; - perror ("getline"); + error (0, errno, "getline"); continue; } @@ -321,7 +331,7 @@ main () if (argc != 2) fprintf (stderr, "Usage: cd DIRECTORY\n"); else if (chdir (argv[1])) - perror ("chdir"); + error (0, errno, "chdir"); } else if (!strcmp (argv[0], "exec")) { @@ -343,7 +353,7 @@ main () if (execv (program, &argv[1]) == 0) fprintf (stderr, "execv (%s) returned 0!\n", program); else - perror ("execv"); + error (0, errno, "execv"); } } else if (!strcmp (argv[0], "setenv")) @@ -351,7 +361,7 @@ main () if (argc != 3) fprintf (stderr, "Usage: setenv VAR VALUE\n"); else if (setenv (argv[1], argv[2], 1)) - perror ("setenv"); + error (0, errno, "setenv"); } else if (!strcmp (argv[0], "fork")) { @@ -359,7 +369,7 @@ main () switch (pid) { case -1: - perror ("fork"); + error (0, errno, "fork"); break; case 0: printf ("I am the child, PID %d.\n", (int) getpid ()); @@ -377,5 +387,3 @@ main () fflush (stderr); } } - - diff --git a/utils/showtrans.c b/utils/showtrans.c index d6acfd9d..aa55f14d 100644 --- a/utils/showtrans.c +++ b/utils/showtrans.c @@ -1,8 +1,7 @@ /* Show files' passive translators. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> + Copyright (C) 1995,96,97,98,99,2001,02 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 @@ -25,11 +24,13 @@ #include <argp.h> #include <fcntl.h> #include <unistd.h> +#include <version.h> +#include <sys/mman.h> #include <error.h> #include <argz.h> -char *argp_program_version = "showtrans 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (showtrans); static struct argp_option options[] = { @@ -42,13 +43,13 @@ static struct argp_option options[] = }; static char *args_doc = "FILE..."; - -static char *doc = "A FILE argument of `-' prints the translator on the node \ -attached to standard input."; +static char *doc = "Show the passive translator of FILE..." +"\vA FILE argument of `-' prints the translator on the node" +" attached to standard input."; /* ---------------------------------------------------------------- */ -void +int main (int argc, char *argv[]) { /* The default exit status -- changed to 0 if we find any translators. */ @@ -66,7 +67,7 @@ main (int argc, char *argv[]) else { char buf[1024], *trans = buf; - int trans_len = sizeof (buf); + size_t trans_len = sizeof (buf); error_t err = file_get_translator (node, &trans, &trans_len); switch (err) @@ -76,14 +77,15 @@ main (int argc, char *argv[]) argz_stringify (trans, trans_len, ' '); if (!silent) - if (print_prefix) - printf ("%s: %s\n", name, trans); - else - puts (trans); + { + if (print_prefix) + printf ("%s: %.*s\n", name, (int) trans_len, trans); + else + printf ("%.*s\n", (int) trans_len, trans); + } if (trans != buf) - vm_deallocate (mach_task_self (), - (vm_address_t)trans, trans_len); + munmap (trans, trans_len); status = 0; @@ -138,5 +140,5 @@ main (int argc, char *argv[]) argp_parse (&argp, argc, argv, 0, 0, 0); - exit (status); + return status; } diff --git a/utils/storecat.c b/utils/storecat.c new file mode 100644 index 00000000..7f9de491 --- /dev/null +++ b/utils/storecat.c @@ -0,0 +1,73 @@ +/* Write a store to stdout + + Copyright (C) 1996, 1997, 2001, 2002 Free Software Foundation, Inc. + Written by Miles Bader <miles@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, USA. */ + +#include <unistd.h> +#include <stdlib.h> +#include <argp.h> +#include <error.h> + +#include <hurd/store.h> +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (storecat); + +int +main (int argc, char **argv) +{ + error_t err; + struct store *s; + char *name; + store_offset_t addr; + store_offset_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 index ba6d0654..a738d50d 100644 --- a/utils/storeinfo.c +++ b/utils/storeinfo.c @@ -1,8 +1,8 @@ /* Show where a file exists - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2001,02 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + 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 @@ -25,22 +25,24 @@ #include <argp.h> #include <unistd.h> #include <sys/fcntl.h> +#include <version.h> #include <error.h> #include <hurd/fs.h> #include <hurd/store.h> -char *argp_program_version = "storeinfo 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (storeinfo); static struct argp_option options[] = { - {"kind", 'k', 0, 0, "Print the type of store behind FILE"}, + {"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"}, - {"runs", 'r', 0, 0, "Print the runs of blocks in 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"}, @@ -48,26 +50,28 @@ static struct argp_option options[] = {0, 0} }; static char *args_doc = "FILE..."; -static char *doc = "With no FILE arguments, the file attached to standard \ -input is used. The fields to be printed are separated by colons, in this \ -order: PREFIX: KIND: NAME: BLOCK-SIZE: BLOCKS: SIZE: RUNS. 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."; +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_KIND 0x02 +#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 0xFF +#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. */ @@ -87,7 +91,7 @@ print_store (struct store *store, int level, unsigned what) putchar (' '); } } - void pstr (char *str, unsigned mask) + void pstr (const char *str, unsigned mask) { if ((what & mask) == mask) { @@ -95,12 +99,20 @@ print_store (struct store *store, int level, unsigned what) fputs (str ?: "-", stdout); } } - void pint (off_t val, unsigned mask) + void psiz (size_t val, unsigned mask) { if ((what & mask) == mask) { psep (); - printf ("%ld", val); + printf ("%zu", val); + } + } + void poff (store_offset_t val, unsigned mask) + { + if ((what & mask) == mask) + { + psep (); + printf ("%Ld", val); } } @@ -111,9 +123,9 @@ print_store (struct store *store, int level, unsigned what) putchar (' '); } - pstr (store->class->name,W_KIND); + pstr (store->class->name,W_TYPE); - if (store->flags && (what & W_KIND)) + if ((store->flags & ~STORE_INACTIVE) && (what & W_FLAGS)) { int t = 0; /* flags tested */ int f = 1; @@ -140,20 +152,20 @@ print_store (struct store *store, int level, unsigned what) pf (STORE_ENFORCED, "enf"); pf (STORAGE_MUTATED, "mut"); - if (store->flags & ~t) + if (store->flags & ~(t | STORE_INACTIVE)) /* Leftover flags. */ { if (! f) putchar (';'); - printf ("0x%x", store->flags); + printf ("0x%x", store->flags & ~(t | STORE_INACTIVE)); } - putchar ('('); + putchar (')'); } pstr (store->name, W_NAME); - pint (store->block_size, W_BLOCK_SIZE); - pint (store->blocks, W_BLOCKS); - pint (store->size, W_SIZE); + psiz (store->block_size, W_BLOCK_SIZE); + poff (store->blocks, W_BLOCKS); + poff (store->size, W_SIZE); if (what & W_RUNS) { @@ -163,9 +175,10 @@ print_store (struct store *store, int level, unsigned what) if (i > 0) putchar (','); if (store->runs[i].start < 0) - printf ("[%ld]", store->runs[i].length); + /* A hole */ + printf ("@+%Ld", store->runs[i].length); else - printf ("%ld[%ld]", store->runs[i].start, store->runs[i].length); + printf ("%Ld+%Ld", store->runs[i].start, store->runs[i].length); } } @@ -177,8 +190,8 @@ print_store (struct store *store, int level, unsigned what) print_store (store->children[i], level + 1, what); } -void -main(int argc, char *argv[]) +int +main (int argc, char *argv[]) { int deref = 0, print_prefix = -1; unsigned what = 0; @@ -191,7 +204,7 @@ main(int argc, char *argv[]) struct store *store; if (file == MACH_PORT_NULL) - error (3, err, source); + error (3, err, "%s", source); if (print_prefix < 0) /* By default, only print filename prefixes for multiple files. */ @@ -200,9 +213,14 @@ main(int argc, char *argv[]) if (what == 0) what = W_ALL; - err = store_create (file, 0, 0, &store); + /* The STORE_NO_FILEIO flag tells it to give us the special + "unknown" class instead of an error if it cannot parse the + file_get_storage_info results. That will allow us to display + what we can from them, i.e. the name that shows at least some + of what the unknown data looked like. */ + err = store_create (file, STORE_INACTIVE|STORE_NO_FILEIO, 0, &store); if (err) - error (4, err, source); + error (4, err, "%s", source); print_store (store, 0, what); store_free (store); @@ -214,12 +232,13 @@ main(int argc, char *argv[]) case 'p': print_prefix = 1; break; case 'P': print_prefix = 0; break; - case 'k': what |= W_KIND; 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 'r': what |= W_RUNS; break; + case 'l': what |= W_RUNS; break; case 'c': what |= W_CHILDREN; break; case ARGP_KEY_NO_ARGS: @@ -245,5 +264,5 @@ main(int argc, char *argv[]) argp_parse (&argp, argc, argv, 0, 0, 0); - exit(0); + return 0; } diff --git a/utils/storeread.c b/utils/storeread.c new file mode 100644 index 00000000..df381bec --- /dev/null +++ b/utils/storeread.c @@ -0,0 +1,130 @@ +/* Write portions of a store to stdout + + Copyright (C) 1996,97,99,2001,02,03,04 Free Software Foundation, Inc. + Written by Miles Bader <miles@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, 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> +#include <version.h> + +const char *argp_program_version = STANDARD_HURD_VERSION (storeread); + +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} +}; +const char arg_doc[] = "FILE [ADDR [LENGTH]]..."; +const char doc[] = "Write portions of the contents of a store to stdout" +"\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; + store_offset_t addr = -1; + int dumped = 0, use_file_io = 0, block_size = 0; + + void dump (store_offset_t addr, ssize_t len) + { + char buf[4096]; + void *data = buf; + size_t data_len = sizeof (buf); + + /* XXX: store->size can be too big for len. */ + 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 (3, err, "%s", arg); + } + else if (addr < 0) + addr = atoll (arg); + else + { + dump (addr, atoi (arg)); + dumped = 1; + addr = -1; + } + break; + + case ARGP_KEY_END: + if (!store) + argp_usage (state); + + 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 deleted file mode 100644 index de12917e..00000000 --- a/utils/su.c +++ /dev/null @@ -1,481 +0,0 @@ -/* `su' for GNU Hurd. - Copyright (C) 1994, 1995, 1996 Free Software Foundation - Written by Roland McGrath. - -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 the GNU Hurd; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - Unlike Unix su, this program does not spawn a subshell. Instead, it - adds or removes (with the -r flag) auth rights to all the processes in - the current login. Note that the addition and removal of rights to a - particular process is voluntary; the process doesn't get them unless - it's listening for the right rpc (which the C library normally does), - and it doesn't have to release any of the rights it has when requested - (though the C library does this automatically in most programs). - - The -r flag allows you to easily be authorized or not authorized for any - number of users (uids and gid sets). This program is not intelligent, - however. The -r flag always removes whatever gids the specified user - has. If some other user you have obtained authorization for has some of - the same gids, it will remove them. The C library could be intelligent - enough to look up the groups of all the uids it has, and make sure it - has at least those. */ - -#include <stdlib.h> -#include <hurd.h> -#include <pwd.h> -#include <grp.h> -#include <termios.h> -#include <unistd.h> -#include <stdio.h> -#include <string.h> -#include <getopt.h> -#include <hurd/msg.h> -#include <assert.h> - -process_t proc; /* Our proc server port. */ - -/* Command line flags. */ -int remove_ids = 0; /* -r: Remove ids instead of adding them. */ - -const struct option longopts[] = - { - { "remove", 0, 0, 'r' }, - { "uid", 0, 0, 'u' }, - { "gid", 0, 0, 'g' }, - { "loginuser", 0, 0, 'l' }, - { "pid", 0, 0, 'p' }, - { "pgrp", 0, 0, 'G' }, - { "loginid", 0, 0, 'i' }, - { 0, } - }; - -struct auth - { - unsigned int *ids; - auth_t authport; - }; - -/* These functions return a malloc'd array of ids that can be passed to - make_auth_handle or used in del_auth messages. They check authorization - and return NULL on failure. */ -unsigned int *get_uid (const char *user); /* From a named user or UID. */ -unsigned int *get_gid (const char *user); /* From a named group or GID. */ -unsigned int *get_user (const char *user); /* All ids for a named user. */ - -int apply_auth (struct auth *auth, pid_t, int); -int apply_auth_to_pids (struct auth *auth, unsigned int, pid_t *, int); -int apply_auth_to_pgrp (struct auth *auth, pid_t); -int apply_auth_to_loginid (struct auth *auth, int); - -int check_password (const char *name, const char *password); - - -int -main (int argc, char **argv) -{ - int c; - int status; - enum { user, group, loginuser, loginid, pid, pgrp } mode = loginuser; - struct auth auth[argc]; - unsigned int authidx = 0, loginidx = 0, pididx = 0, pgrpidx = 0; - int loginids[argc]; - pid_t pids[argc], pgrps[argc]; - unsigned int i, j; - - proc = getproc (); /* Used often. */ - - while ((c = getopt_long (argc, argv, "--rgulpiG", longopts, NULL)) != EOF) - switch (c) - { - case 'r': - remove_ids = 1; - break; - - case 'u': - mode = user; - break; - case 'g': - mode = group; - break; - case 'l': - mode = loginuser; - break; - case 'p': - mode = pid; - break; - case 'i': - mode = loginid; - break; - case 'G': - mode = pgrp; - break; - - case 1: /* Non-option argument. */ - switch (mode) - { - case user: - auth[authidx++].ids = get_uid (optarg); - break; - case group: - auth[authidx++].ids = get_gid (optarg); - break; - case loginuser: - auth[authidx++].ids = get_user (optarg); - break; - - case pid: - pids[pididx++] = atoi (optarg); - break; - case loginid: - loginids[loginidx++] = atoi (optarg); - break; - case pgrp: - pgrps[pgrpidx++] = atoi (optarg); - break; - } - break; - - case '?': - default: - fprintf (stderr, "Usage: %s [-r|--remove]\n\ - [-u|--uid UID...] [-g|--gid GID...] [-l|--loginuser USER...]\n\ - [-p|--pid PID...] [-i|--loginid ID...] [-G|--pgrp PGRP...]\n", - program_invocation_short_name); - exit (1); - } - - if (authidx == 0) - /* No ids specified; default is "su root". */ - auth[authidx++].ids = get_user ("root"); - - if (pididx == 0 && loginidx == 0 && pgrpidx == 0) - { - /* No processes specified; default is current login collection. */ - errno = proc_getloginid (proc, getpid (), &loginids[loginidx++]); - if (errno) - { - perror ("proc_getloginid"); - return 1; - } - } - - status = 0; - - if (! remove_ids) - for (i = 0; i < authidx; ++i) - { - struct auth *a = &auth[i]; - errno = auth_makeauth (getauth (), NULL, MACH_MSG_TYPE_COPY_SEND, 0, - &a->ids[1], a->ids[0], NULL, 0, - &a->ids[1 + a->ids[0] + 1], - a->ids[1 + a->ids[0]], NULL, 0, &a->authport); - if (errno) - { - perror ("auth_makeauth"); - status = 1; - } - } - - for (j = 0; j < authidx; ++j) - status |= apply_auth_to_pids (&auth[j], pididx, pids, 0); - - for (i = 0; i < loginidx; ++i) - { - for (j = 0; j < authidx; ++j) - status |= apply_auth_to_loginid (&auth[j], loginids[i]); - } - - for (i = 0; i < pgrpidx; ++i) - { - for (j = 0; j < authidx; ++j) - status |= apply_auth_to_loginid (&auth[j], pgrps[i]); - } - - - return status; -} - -/* Turn USER into a list of ids, giving USER's primary uid and gid from the - passwd file and all the groups that USER appears in in the group file. */ - -unsigned int * -get_user (const char *user) -{ - struct passwd *p; - unsigned int ngids, *ids; - - p = getpwnam (user); - if (p == NULL) - { - fprintf (stderr, "%s: User `%s' not found\n", - program_invocation_short_name, user); - return NULL; - } - - if (! check_password (user, p->pw_passwd)) - return NULL; - - /* We don't need to change our own gids, but it doesn't really hurt, - and initgroups does all the work for us. */ - if (initgroups (user, p->pw_gid) < 0) - { - perror ("initgroups"); - return NULL; - } - ngids = getgroups (0, NULL); - if ((int) ngids < 0) - { - getgroups_lost: - perror ("getgroups"); - return NULL; - } - assert (sizeof (*ids) == sizeof (gid_t)); - ids = malloc ((3 + ngids) * sizeof (*ids)); - ids[0] = 1; - ids[1] = p->pw_uid; - ids[2] = getgroups (ngids, &ids[3]); - if ((int) ids[2] < 0) - goto getgroups_lost; - - return ids; -} - -/* Return an id list containing just the uid of USER. */ - -unsigned int * -get_uid (const char *user) -{ - struct passwd *p; - unsigned int *ids; - uid_t uid; - char *uend; - - uid = strtoul (user, &uend, 10); - if (uend && *uend == '\0') - { - if (remove_ids || getuid () == 0) - /* No need to verify. */ - p = NULL; - else - { - p = getpwuid (uid); - if (p == NULL) - { - fprintf (stderr, "%s: UID %u not found\n", - program_invocation_short_name, uid); - return NULL; - } - } - } - else - { - p = getpwnam (user); - if (p == NULL) - { - fprintf (stderr, "%s: User `%s' not found\n", - program_invocation_short_name, user); - return NULL; - } - } - - if (p != NULL && ! check_password (user, p->pw_passwd)) - return NULL; - - ids = malloc (3 * sizeof (*ids)); - ids[0] = 1; - ids[1] = p->pw_uid; - ids[2] = 0; - - return ids; -} - -/* Return an id list containing just the gid of GROUP. */ - -unsigned int * -get_gid (const char *group) -{ - struct group *g; - unsigned int *ids; - gid_t gid; - char *gend; - - gid = strtoul (group, &gend, 10); - if (gend && *gend == '\0') - { - if (remove_ids || getuid () == 0) - /* No need to verify. */ - g = NULL; - else - { - g = getgrgid (gid); - if (g == NULL) - { - fprintf (stderr, "%s: GID %u not found\n", - program_invocation_short_name, gid); - return NULL; - } - } - } - else - { - g = getgrnam (group); - if (g == NULL) - { - fprintf (stderr, "%s: Group `%s' not found\n", - program_invocation_short_name, group); - return NULL; - } - } - - if (g != NULL && ! check_password (group, g->gr_passwd)) - return NULL; - - ids = malloc (3 * sizeof (*ids)); - ids[0] = 0; - ids[1] = 1; - ids[2] = g->gr_gid; - - return ids; -} - -/* Add or delete (under -r) the ids indicated by AUTH to/from PID. If - IGNORE_BAD_PID is nonzero, return success if PID does not exist. - Returns zero if successful, nonzero on error (after printing message). */ - -int -apply_auth (struct auth *auth, pid_t pid, int ignore_bad_pid) -{ - error_t err; - - if (! auth->ids) - return 0; - - err = HURD_MSGPORT_RPC (proc_getmsgport (proc, pid, &msgport), - proc_pid2task (proc, pid, &refport), 1, - remove_ids ? - msg_del_auth (msgport, refport, - &auth->ids[1], auth->ids[0], - &auth->ids[1 + auth->ids[0] + 1], - auth->ids[1 + auth->ids[0]]) : - msg_add_auth (msgport, auth->authport)); - if (err && - (!ignore_bad_pid || (err != ESRCH && err != MIG_SERVER_DIED))) - { - fprintf (stderr, "%s: error in %s_auth from PID %d: %s\n", - program_invocation_short_name, - remove_ids ? "del" : "add", pid, strerror (err)); - return 1; - } - else - return 0; -} - -int -apply_auth_to_pids (struct auth *auth, unsigned int npids, pid_t pids[], - int ignore_bad_pid) -{ - int status = 0; - unsigned int i; - - for (i = 0; i < npids; ++i) - status |= apply_auth (auth, pids[i], ignore_bad_pid); - - return status; -} - -int -apply_auth_to_loginid (struct auth *auth, int loginid) -{ - unsigned int npids = 20; - pid_t pidbuf[20], *pids = pidbuf; - int status; - error_t err; - - err = proc_getloginpids (proc, loginid, &pids, &npids); - if (err) - { - fprintf (stderr, "%s: proc_getloginpids failed for loginid %d: %s\n", - program_invocation_short_name, loginid, strerror (err)); - return 1; - } - - status = apply_auth_to_pids (auth, npids, pids, 1); - - if (pids != pidbuf) - vm_deallocate (mach_task_self (), - (vm_address_t) pids, npids * sizeof (pid_t)); - - return status; -} - -int -apply_auth_to_pgrp (struct auth *auth, pid_t pgrp) -{ - unsigned int npids = 20; - pid_t pidbuf[20], *pids = pidbuf; - int status; - error_t err; - - err = proc_getpgrppids (proc, pgrp, &pids, &npids); - if (err) - { - fprintf (stderr, "%s: proc_getpgrppids failed for pgrp %d: %s\n", - program_invocation_short_name, pgrp, strerror (err)); - return 1; - } - - status = apply_auth_to_pids (auth, npids, pids, 1); - - if (pids != pidbuf) - vm_deallocate (mach_task_self (), - (vm_address_t) pids, npids * sizeof (pid_t)); - - return status; -} - -/* Return 1 if the user gives the correct password matching the encrypted - string PASSWORD, 0 if not. Return 1 without asking for a password if - run by uid 0 or if PASSWORD is an empty password, and always under -r. - Always prints a message before returning 0. */ -int -check_password (const char *name, const char *password) -{ - extern char *crypt (const char *string, const char salt[2]); -#pragma weak crypt - char *unencrypted, *encrypted; - static char *prompt = NULL; - - if (remove_ids || getuid () == 0 || password == NULL || password[0] == '\0') - return 1; - - asprintf (&prompt, "%s's Password:", name); - unencrypted = getpass (prompt); - if (crypt) - { - encrypted = crypt (unencrypted, password); - memset (unencrypted, 0, strlen (unencrypted)); /* Paranoia may destroya. */ - } - else - encrypted = unencrypted; - - if (!strcmp (encrypted, password)) - return 1; - - fprintf (stderr, "%s: Access denied for `%s'\n", - program_invocation_short_name, name); - return 0; -} diff --git a/utils/sush.sh b/utils/sush.sh index 034fb6ad..4fccd75e 100644 --- a/utils/sush.sh +++ b/utils/sush.sh @@ -1,3 +1,90 @@ #!/bin/sh -# A unix-like su (one which invokes a sub-shell). -exec /bin/login --program-name="$0" -pxSLf -aHOME -aMOTD -aUMASK -aBACKUP_SHELLS "$@" +# 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/sync.c b/utils/sync.c deleted file mode 100644 index bce972e8..00000000 --- a/utils/sync.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Call sync synchronously. - Copyright (C) 1994, 1995 Free Software Foundation, Inc. - Written by Michael I. Bushnell. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include <hurd.h> - -int -main () -{ - __USEPORT (CRDIR, __file_syncfs (port, 1, 1)); - return 0; -} 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..467cf46f --- /dev/null +++ b/utils/unsu.c @@ -0,0 +1,90 @@ +/* Attempt to undo a previous su + + Copyright (C) 1997,98,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 <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 setauth --save" + "\vThis command is convenient, but will only correctly undo a limited" + " subset of possible setauth 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 index 541996b3..8e52c81b 100644 --- a/utils/uptime.sh +++ b/utils/uptime.sh @@ -1,2 +1,62 @@ #!/bin/sh -/bin/w -u +# 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..6ead677e --- /dev/null +++ b/utils/vminfo.c @@ -0,0 +1,241 @@ +/* Print task vm information + + Copyright (C) 1996,97,98,2002 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 <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 (" [%#zx] (hole)\n", addr - hole_addr); + else + printf (" [%zd] (hole)\n", addr - hole_addr); + } + else if ((what & (W_ADDRS|W_SIZES)) == W_SIZES) + { + if (hex) + printf ("%#10zx (hole)\n", addr - hole_addr); + else + printf ("%10zu (hole)\n", addr - hole_addr); + } + } + + if ((what & (W_ADDRS|W_SIZES)) == (W_ADDRS|W_SIZES)) + if (hex) + printf ("%#10zx[%#zx]", addr, size); + else + printf ("%10zu[%zd]", addr, size); + else if ((what & (W_ADDRS|W_SIZES)) == W_ADDRS) + if (hex) + printf ("%#10zx", addr); + else + printf ("%10zu", addr); + else if ((what & (W_ADDRS|W_SIZES)) == W_SIZES) + { + if (hex) + printf ("%#10zx", size); + else + printf ("%10zu", 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=%#zx", offs); + else + printf (", offs=%zd", offs); + } + putchar (')'); + } + putchar ('\n'); + + addr += size; + } + + exit (0); +} diff --git a/utils/vmstat.c b/utils/vmstat.c index c7f7a7af..7d852992 100644 --- a/utils/vmstat.c +++ b/utils/vmstat.c @@ -1,8 +1,7 @@ /* Print vm statistics - Copyright (C) 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> + Copyright (C) 1996,97,99,2002 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 @@ -25,30 +24,30 @@ #include <string.h> #include <unistd.h> #include <stdlib.h> +#include <fcntl.h> +#include <version.h> #include <mach.h> #include <mach/vm_statistics.h> #include <mach/default_pager.h> #include <hurd.h> +#include <hurd/paths.h> -char *argp_program_version = "vmstat 1.1 (GNU " HURD_RELEASE ")"; +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", 1 }, + {"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"}, - - /* A header for all the individual field options. */ - { 0,0,0,0, "Selecting which statistics to show:", 2}, - {0} }; static const char *args_doc = "[PERIOD [COUNT [HEADER_INTERVAL]]]"; -static const char *doc = "If PERIOD is supplied, then terse mode is" +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" @@ -61,6 +60,7 @@ static const char *doc = "If PERIOD is supplied, then terse mode is" 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 @@ -71,6 +71,31 @@ enum val_type 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 @@ -80,13 +105,13 @@ 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); + printf (sign ? "%+*lld%%" : "%*lld%%", fwidth - 1, val); else if ((type == SIZE || type == PAGESZ) && size_units == 0) { float fval = val; char *units = " KMGT", *u = units; - while (fval > 1024) + while (fval >= 10000) { fval /= 1024; u++; @@ -101,10 +126,14 @@ print_val (val_t val, enum val_type type, { if ((type == SIZE || type == PAGESZ) && size_units > 0) val /= size_units; - printf (sign ? "%+*d" : "%*d", fwidth, val); + printf (sign ? "%+*lld" : "%*lld", 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 { @@ -117,15 +146,15 @@ struct vm_state; /* fwd */ struct field { - /* Name of the field; used for the option name. */ + /* Name of the field. */ char *name; - /* A descriptive title used for long output format. */ - char *desc; - /* 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; @@ -134,6 +163,9 @@ struct field 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; @@ -148,7 +180,7 @@ struct field }; /* State about system vm from which we compute the above defined fields. */ -struct vm_state +struct vm_state { /* General vm statistics. */ struct vm_statistics vmstats; @@ -167,7 +199,7 @@ vm_state_refresh (struct vm_state *state) return err; /* Mark the info as invalid, but leave DEF_PAGER alone. */ - bzero (&state->def_pager_info, sizeof state->def_pager_info); + memset (&state->def_pager_info, 0, sizeof state->def_pager_info); return 0; } @@ -198,7 +230,7 @@ vm_state_get_field (struct vm_state *state, const struct field *field) } static val_t -get_cache_hit_ratio (struct vm_state *state, const struct field *field) +get_memobj_hit_ratio (struct vm_state *state, const struct field *field) { return state->vmstats.hits * 100 / state->vmstats.lookups; } @@ -215,89 +247,125 @@ ensure_def_pager_info (struct vm_state *state) mach_port_t host; err = get_privileged_ports (&host, 0); - if (err) + if (err == EPERM) { - error (0, err, "get_privileged_ports"); - return 0; + /* We are not root, so try opening the /servers file. */ + state->def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0); + if (state->def_pager == MACH_PORT_NULL) + { + error (0, errno, _SERVERS_DEFPAGER); + return 0; + } } + if (state->def_pager == MACH_PORT_NULL) + { + 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); + 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 (err) + if (!MACH_PORT_VALID (state->def_pager)) + { + if (state->def_pager == MACH_PORT_NULL) { - error (0, err, "vm_set_default_memory_manager"); - return 0; + 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); } -static val_t -get_swap_size (struct vm_state *state, const struct field *field) -{ - return - ensure_def_pager_info (state) ? state->def_pager_info.dpi_total_space : -1; -} +#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; } -static val_t -get_swap_free (struct vm_state *state, const struct field *field) -{ - return - ensure_def_pager_info (state) ? state->def_pager_info.dpi_free_space : -1; -} - -static val_t -get_swap_page_size (struct vm_state *state, const struct field *field) -{ - return - ensure_def_pager_info (state) ? state->def_pager_info.dpi_page_size : -1; -} - -static val_t -get_swap_active (struct vm_state *state, const struct field *field) -{ - return - ensure_def_pager_info (state) - ? (state->def_pager_info.dpi_total_space - - state->def_pager_info.dpi_free_space) - : -1; -} +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", "Pagesize", " pgsz", CONST,PAGESZ, 1,_F(pagesize)}, - {"size", "Size", " size", CONST,SIZE, 1,0,get_size}, - {"free", "Free", " free", VARY, SIZE, 1,_F(free_count)}, - {"active", "Active", " actv", VARY, SIZE, 1,_F(active_count)}, - {"inactive", "Inactive", "inact", VARY, SIZE, 1,_F(inactive_count)}, - {"wired", "Wired", "wired", VARY, SIZE, 1,_F(wire_count)}, - {"zero-filled", "Zeroed", "zeroed", CUMUL,SIZE, 1,_F(zero_fill_count)}, - {"reactivated", "Reactivated", "react", CUMUL,SIZE, 1,_F(reactivations)}, - {"pageins", "Pageins", "pgins", CUMUL,SIZE, 1,_F(pageins)}, - {"pageouts", "Pageouts", "pgouts", CUMUL,SIZE, 1,_F(pageouts)}, - {"faults", "Faults", "pfaults",CUMUL,COUNT,1,_F(faults)}, - {"cow-faults", "Cow faults", "cowpfs", CUMUL,COUNT,1,_F(cow_faults)}, - {"cache-lookups","Cache lookups","clkups", CUMUL,COUNT,0,_F(lookups)}, - {"cache-hits", "Cache hits", "chits", CUMUL,COUNT,0,_F(hits)}, - {"cache-hit-ratio","Cache hit ratio","chrat",VARY,PCENT,1,-1,get_cache_hit_ratio}, - {"swap-size", "Swap size", "swsize", CONST,SIZE, 1,0,get_swap_size}, - {"swap-active", "Swap active", "swactv", VARY, SIZE, 0,0,get_swap_active}, - {"swap-free", "Swap free", "swfree", VARY, SIZE, 1,0,get_swap_free}, - {"swap-pagesize","Swap pagesize","swpgsz", CONST,PAGESZ, 0,0,get_swap_page_size}, + {"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) { @@ -327,7 +395,7 @@ main (int argc, char **argv) case 'H': print_heading = 0; break; case 'b': size_units = 1; break; case 'v': size_units = -1; break; - case 'k': size_units = 1024; break; + case 'k': size_units = K; break; case ARGP_KEY_ARG: terse = 1; @@ -352,8 +420,9 @@ main (int argc, char **argv) struct argp_option *field_opts; int field_opts_size; struct argp field_argp = { 0, parse_opt }; - const struct argp *parents[] = { &field_argp, 0 }; - const struct argp argp = { options, parse_opt, args_doc, doc, parents }; + 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++) @@ -362,19 +431,19 @@ main (int argc, char **argv) /* 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); + memset (field_opts, 0, field_opts_size); for (field = fields; field->name; field++) { int which = field - fields; struct argp_option *opt = &field_opts[which]; - opt->name = field->name; + opt->name = name_to_option (field->name); opt->key = -1 - which; /* options are numbered -1 ... -(N - 1). */ - opt->doc = field->desc; + opt->doc = field->doc; opt->group = 2; } - /* No need to terminate FIELD_OPTS because the bzero above's done so. */ + /* No need to terminate FIELD_OPTS because the memset above has done so. */ field_argp.options = field_opts; @@ -387,10 +456,10 @@ main (int argc, char **argv) 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 \ + /* Returns an appropriate 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 @@ -399,9 +468,15 @@ main (int argc, char **argv) (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. */ + memset (&state, 0, sizeof (state)); /* Initialize STATE. */ err = vm_state_refresh (&state); if (err) error (2, err, "vm_state_refresh"); @@ -429,6 +504,9 @@ main (int argc, char **argv) do { + int num; + int fwidths[num_fields]; + if (first_hdr) first_hdr = 0; else @@ -447,7 +525,7 @@ main (int argc, char **argv) else { PSEP (", ", "("); - printf ("%s: ", field->desc); + printf ("%s: ", field->name); PVAL (val, field, 0, 0); } } @@ -455,39 +533,62 @@ main (int argc, char **argv) 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, first = 1; field->name; field++) + for (field = fields, num = 0, first = 1; field->name; field++, num++) if (output_fields & (1 << (field - fields))) { PSEP (" ", 0); - fputs (field->hdr, stdout); + fprintf (stdout, "%*s", fwidths[num], field->hdr); } putchar ('\n'); } - + prev_state = state; for (repeats = 0 - ; count && repeats < hdr_interval && count + ; count && repeats < hdr_interval ; repeats++, count--) { /* Output the fields. */ - for (field = fields, first = 1; field->name; field++) + for (field = fields, num = 0, first = 1; field->name; field++, num++) if (output_fields & (1 << (field - fields))) { - int sign = 0; - int width = strlen (field->hdr); val_t val = vm_state_get_field (&state, field); - if (repeats && field->change_type == CUMUL) + if (val < 0) + /* Couldn't fetch this field, don't try again. */ + const_fields &= ~(1 << (field - fields)); + else { - sign = 1; - val -= vm_state_get_field (&prev_state, field); - } + int sign = 0; - PSEP (" ", 0); - PVAL (val, field, width, sign); + 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'); @@ -507,7 +608,7 @@ main (int argc, char **argv) else /* Verbose output. */ { - int max_desc_width = 0; + int max_width = 0; if (print_prefix < 0) /* By default, only print a prefix if there are multiple fields. */ @@ -518,23 +619,26 @@ main (int argc, char **argv) for (field = fields; field->name; field++) if (output_fields & (1 << (field - fields))) { - int desc_len = strlen (field->desc); - if (desc_len > max_desc_width) - max_desc_width = desc_len; + 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); - int fwidth = 0; - if (print_prefix) + if (val >= 0) { - printf ("%s:", field->desc); - fwidth = max_desc_width + 5 - strlen (field->desc); + int fwidth = 0; + if (print_prefix) + { + printf ("%s: ", field->name); + fwidth = max_width - strlen (field->name); + } + PVAL (val, field, fwidth, 0); + putchar ('\n'); } - PVAL (val, field, fwidth, 0); - putchar ('\n'); } } @@ -1,8 +1,8 @@ /* Hurdish w - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2001,02 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + 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 @@ -22,6 +22,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include <paths.h> #include <ctype.h> @@ -29,6 +30,7 @@ #include <pwd.h> #include <grp.h> #include <netdb.h> +#include <version.h> #include <sys/fcntl.h> @@ -42,13 +44,13 @@ #include "psout.h" -#define DEFAULT_FMT_STRING "%^%user %tty %from %login %idle %what" +#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 (); -char *argp_program_version = "w 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (w); #define OA OPTION_ARG_OPTIONAL @@ -71,7 +73,7 @@ static struct argp_option options[] = {0, 0} }; static char *args_doc = "[USER...]"; -static char *doc = 0; +static char *doc = "Show logged in users and what they are doing"; /* The current time of day. */ static struct timeval now; @@ -93,6 +95,14 @@ struct w_hook #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; @@ -121,23 +131,34 @@ w_fetch (struct proc_stat *ps, ps_flags_t need, ps_flags_t have) if (sd) *sd = '\0'; } - - have |= W_PSTAT_HOST; } + have |= W_PSTAT_HOST; } if (need & W_PSTAT_IDLE) { - struct stat stat; - struct ps_tty *tty = ps->tty; - - hook->idle.tv_usec = 0; - if (io_stat (tty->port, &stat) == 0) + if (have & PSTAT_TTY) { - hook->idle.tv_sec = now.tv_sec - stat.st_mtime; - if (hook->idle.tv_sec > 0) - have |= W_PSTAT_IDLE; + 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) @@ -204,21 +225,22 @@ w_get_host (struct proc_stat *ps, char **host, unsigned *host_len) { struct w_hook *hook = ps->hook; *host = hook->host; - *host_len = strlen (*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 (); +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, 20, -1,0, &w_user_getter, ps_emit_user_name,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, 16, -1,0, &w_host_getter, ps_emit_string, ps_cmp_strings}, - {"Idle", 0, -5, -1,0, &w_idle_getter, ps_emit_minutes,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} }; @@ -231,7 +253,7 @@ 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. */ + appropriate prefix. */ char tty[sizeof _PATH_DEV + sizeof u->ut_line]; io_t tty_node; error_t err; @@ -239,6 +261,19 @@ add_utmp_procs (struct proc_stat_list *procs, struct utmp *u) 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. */ @@ -271,7 +306,7 @@ add_utmp_procs (struct proc_stat_list *procs, struct utmp *u) else { struct proc_stat **pgrp_procs; - unsigned num_procs; + size_t num_procs; err = proc_stat_list_add_pgrp (procs, -pid, &pgrp_procs, &num_procs); if (! err) @@ -301,62 +336,74 @@ add_utmp_procs (struct proc_stat_list *procs, struct utmp *u) tty, pid < 0 ? "pgrp" : "pid", pid < 0 ? -pid : pid); } -/* Read into PROCS from the utmp file called NAME. */ -static void -read_utmp_procs (struct proc_stat_list *procs, char *name) +/* 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) { - int rd, fd; - struct utmp buf[64]; + struct ps_context *context; + struct proc_stat *ps; + error_t err; - fd = open (name, O_RDONLY); - if (fd < 0) - error (9, errno, "%s", name); + 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"); - while ((rd = read (fd, buf, sizeof (buf))) > 0) + err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC); + if (!err && !(ps->flags & PSTAT_TASK_BASIC)) + err = EGRATUITOUS; + if (err) { - struct utmp *u = buf; - while (rd >= sizeof (*u)) - { - if (*u->ut_name && *u->ut_line) - /* An active entry. */ - add_utmp_procs (procs, u); - rd -= sizeof (*u); - u++; - } + 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; } - close (fd); + ps_context_free (context); + + return 0; } - + static void uptime (struct proc_stat_list *procs) { - struct stat st; + 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; } + int maybe_add_user (struct proc_stat *ps) + { if (ps->hook) nusers++; return 0; } proc_stat_list_for_each (procs, maybe_add_user); - /* Until we get a better way of finding out how long the system's been - up... XXX */ - if (stat ("/var/run/uptime", &st) != 0) + if (fetch_boot_time (&boot_time)) strcpy (uptime_rep, "chuck"); else { - struct timeval uptime = { now.tv_sec - st.st_ctime, 0 }; + 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", + 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. */ - errno = ps_host_load_info (&load); - if (errno) - error (0, errno, "ps_host_load_info"); + 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", @@ -365,10 +412,11 @@ uptime (struct proc_stat_list *procs) (double)load->avenrun[2] / (double)LOAD_SCALE); } -void +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; @@ -378,12 +426,23 @@ main(int argc, char *argv[]) #if 0 char *tty_names = 0; unsigned num_tty_names = 0; - struct idvec *only_uids = make_idvec (), *not_uids = make_idvec (); #endif - struct ps_user_hooks ps_hooks = { 0, w_fetch, w_cleanup }; + 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) { @@ -400,6 +459,19 @@ main(int argc, char *argv[]) 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: @@ -424,18 +496,25 @@ main(int argc, char *argv[]) /* Parse our options. */ argp_parse (&argp, argc, argv, 0, 0, 0); - read_utmp_procs (procs, _PATH_UTMP); + /* 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); + squash_bogus_fields, squash_nominal_fields, 0); - exit(0); + return 0; } @@ -55,8 +55,8 @@ static struct argp_option options[] = {0, 0} }; static char *args_doc = "[USER...]"; -static char *doc = - "A USER specified as an argument adds (or removes) that user's groups as" +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."; |