(WRITTEN_BY): Rename from AUTHORS.
[platform/upstream/coreutils.git] / src / su.c
index fdb43cb..94594b2 100644 (file)
--- a/src/su.c
+++ b/src/su.c
@@ -1,5 +1,5 @@
 /* su for GNU.  Run a shell with substitute user and group IDs.
-   Copyright (C) 92, 93, 94, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1992-2003 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
 #include <sys/types.h>
 #include <pwd.h>
 #include <grp.h>
+
+/* Hide any system prototype for getusershell.
+   This is necessary because some Cray systems have a conflicting
+   prototype (returning `int') in <unistd.h>.  */
+#define getusershell _getusershell_sys_proto_
+
 #include "system.h"
+#include "dirname.h"
 
-#if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG)
-#include <syslog.h>
-#else /* !HAVE_SYSLOG_H */
-#ifdef SYSLOG_SUCCESS
-#undef SYSLOG_SUCCESS
-#endif
-#ifdef SYSLOG_FAILURE
-#undef SYSLOG_FAILURE
-#endif
-#ifdef SYSLOG_NON_ROOT
-#undef SYSLOG_NON_ROOT
+#undef getusershell
+
+#if HAVE_SYSLOG_H && HAVE_SYSLOG
+# include <syslog.h>
+#else
+# undef SYSLOG_SUCCESS
+# undef SYSLOG_FAILURE
+# undef SYSLOG_NON_ROOT
 #endif
-#endif /* !HAVE_SYSLOG_H */
 
-#ifdef _POSIX_VERSION
-#include <limits.h>
-#else /* not _POSIX_VERSION */
-struct passwd *getpwuid ();
-struct group *getgrgid ();
-uid_t getuid ();
-#include <sys/param.h>
-#endif /* not _POSIX_VERSION */
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
 
 #ifndef HAVE_ENDGRENT
 # define endgrent() ((void) 0)
@@ -108,17 +106,34 @@ uid_t getuid ();
 # define endpwent() ((void) 0)
 #endif
 
-#ifdef HAVE_SHADOW_H
-#include <shadow.h>
+#if HAVE_SHADOW_H
+# include <shadow.h>
 #endif
 
 #include "error.h"
 
+/* The official name of this program (e.g., no `g' prefix).  */
+#define PROGRAM_NAME "su"
+
+#define WRITTEN_BY _("Written by David MacKenzie.")
+
+#if HAVE_PATHS_H
+# include <paths.h>
+#endif
+
 /* The default PATH for simulated logins to non-superuser accounts.  */
-#define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
+#ifdef _PATH_DEFPATH
+# define DEFAULT_LOGIN_PATH _PATH_DEFPATH
+#else
+# define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
+#endif
 
 /* The default PATH for simulated logins to superuser accounts.  */
-#define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
+#ifdef _PATH_DEFPATH_ROOT
+# define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT
+#else
+# define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
+#endif
 
 /* The shell to run if none is given in the user's passwd entry.  */
 #define DEFAULT_SHELL "/bin/sh"
@@ -132,22 +147,14 @@ char *getusershell ();
 void endusershell ();
 void setusershell ();
 
-char *basename ();
-char *xmalloc ();
-char *xrealloc ();
-char *xstrdup ();
-
 extern char **environ;
 
+static void run_shell (const char *, const char *, char **)
+     ATTRIBUTE_NORETURN;
+
 /* The name this program was run with.  */
 char *program_name;
 
-/* If nonzero, display usage information and exit.  */
-static int show_help;
-
-/* If nonzero, print the version on standard output and exit.  */
-static int show_version;
-
 /* If nonzero, pass the `-f' option to the subshell.  */
 static int fast_startup;
 
@@ -160,22 +167,22 @@ static int change_environment;
 static struct option const longopts[] =
 {
   {"command", required_argument, 0, 'c'},
-  {"fast", no_argument, &fast_startup, 1},
-  {"help", no_argument, &show_help, 1},
-  {"login", no_argument, &simulate_login, 1},
+  {"fast", no_argument, NULL, 'f'},
+  {"login", no_argument, NULL, 'l'},
   {"preserve-environment", no_argument, &change_environment, 0},
   {"shell", required_argument, 0, 's'},
-  {"version", no_argument, &show_version, 1},
+  {GETOPT_HELP_OPTION_DECL},
+  {GETOPT_VERSION_OPTION_DECL},
   {0, 0, 0, 0}
 };
 
 /* Add VAL to the environment, checking for out of memory errors.  */
 
 static void
-xputenv (const char *val)
+xputenv (char *val)
 {
   if (putenv (val))
-    error (1, 0, _("virtual memory exhausted"));
+    xalloc_die ();
 }
 
 /* Return a newly-allocated string whose contents concatenate
@@ -185,7 +192,7 @@ static char *
 concat (const char *s1, const char *s2, const char *s3)
 {
   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
-  char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+  char *result = xmalloc (len1 + len2 + len3 + 1);
 
   strcpy (result, s1);
   strcpy (result + len1, s2);
@@ -216,35 +223,40 @@ log_su (const struct passwd *pw, int successful)
 {
   const char *new_user, *old_user, *tty;
 
-#ifndef SYSLOG_NON_ROOT
+# ifndef SYSLOG_NON_ROOT
   if (pw->pw_uid)
     return;
-#endif
+# endif
   new_user = pw->pw_name;
   /* The utmp entry (via getlogin) is probably the best way to identify
      the user, especially if someone su's from a su-shell.  */
   old_user = getlogin ();
   if (old_user == NULL)
-    old_user = "";
+    {
+      /* getlogin can fail -- usually due to lack of utmp entry.
+        Resort to getpwuid.  */
+      struct passwd *pwd = getpwuid (getuid ());
+      old_user = (pwd ? pwd->pw_name : "");
+    }
   tty = ttyname (2);
   if (tty == NULL)
-    tty = "";
+    tty = "none";
   /* 4.2BSD openlog doesn't have the third parameter.  */
-  openlog (basename (program_name), 0
-#ifdef LOG_AUTH
+  openlog (base_name (program_name), 0
+# ifdef LOG_AUTH
           , LOG_AUTH
-#endif
+# endif
           );
   syslog (LOG_NOTICE,
-#ifdef SYSLOG_NON_ROOT
+# ifdef SYSLOG_NON_ROOT
          "%s(to %s) %s on %s",
-#else
+# else
          "%s%s on %s",
-#endif
+# endif
          successful ? "" : "FAILED SU ",
-#ifdef SYSLOG_NON_ROOT
+# ifdef SYSLOG_NON_ROOT
          new_user,
-#endif
+# endif
          old_user, tty);
   closelog ();
 }
@@ -259,7 +271,7 @@ static int
 correct_password (const struct passwd *pw)
 {
   char *unencrypted, *encrypted, *correct;
-#ifdef HAVE_SHADOW_H
+#if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
   /* Shadow passwd stuff for SVR3 and maybe other systems.  */
   struct spwd *sp = getspnam (pw->pw_name);
 
@@ -268,7 +280,7 @@ correct_password (const struct passwd *pw)
     correct = sp->sp_pwdp;
   else
 #endif
-  correct = pw->pw_passwd;
+    correct = pw->pw_passwd;
 
   if (getuid () == 0 || correct == 0 || correct[0] == '\0')
     return 1;
@@ -297,7 +309,7 @@ modify_environment (const struct passwd *pw, const char *shell)
       /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
          Unset all other environment variables.  */
       term = getenv ("TERM");
-      environ = (char **) xmalloc (2 * sizeof (char *));
+      environ = xmalloc (2 * sizeof (char *));
       environ[0] = 0;
       if (term)
        xputenv (concat ("TERM", "=", term));
@@ -305,8 +317,9 @@ modify_environment (const struct passwd *pw, const char *shell)
       xputenv (concat ("SHELL", "=", shell));
       xputenv (concat ("USER", "=", pw->pw_name));
       xputenv (concat ("LOGNAME", "=", pw->pw_name));
-      xputenv (concat ("PATH", "=", pw->pw_uid
-                      ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH));
+      xputenv (concat ("PATH", "=", (pw->pw_uid
+                                    ? DEFAULT_LOGIN_PATH
+                                    : DEFAULT_ROOT_LOGIN_PATH)));
     }
   else
     {
@@ -333,13 +346,13 @@ change_identity (const struct passwd *pw)
 #ifdef HAVE_INITGROUPS
   errno = 0;
   if (initgroups (pw->pw_name, pw->pw_gid) == -1)
-    error (1, errno, _("cannot set groups"));
+    error (EXIT_FAILURE, errno, _("cannot set groups"));
   endgrent ();
 #endif
   if (setgid (pw->pw_gid))
-    error (1, errno, _("cannot set group id"));
+    error (EXIT_FAILURE, errno, _("cannot set group id"));
   if (setuid (pw->pw_uid))
-    error (1, errno, _("cannot set user id"));
+    error (EXIT_FAILURE, errno, _("cannot set user id"));
 }
 
 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
@@ -354,23 +367,23 @@ run_shell (const char *shell, const char *command, char **additional_args)
   int argno = 1;
 
   if (additional_args)
-    args = (const char **) xmalloc (sizeof (char *)
+    args = xmalloc (sizeof (char *)
                                    * (10 + elements (additional_args)));
   else
-    args = (const char **) xmalloc (sizeof (char *) * 10);
+    args = xmalloc (sizeof (char *) * 10);
   if (simulate_login)
     {
       char *arg0;
       char *shell_basename;
 
-      shell_basename = basename (shell);
+      shell_basename = base_name (shell);
       arg0 = xmalloc (strlen (shell_basename) + 2);
       arg0[0] = '-';
       strcpy (arg0 + 1, shell_basename);
       args[0] = arg0;
     }
   else
-    args[0] = basename (shell);
+    args[0] = base_name (shell);
   if (fast_startup)
     args[argno++] = "-f";
   if (command)
@@ -383,7 +396,12 @@ run_shell (const char *shell, const char *command, char **additional_args)
       args[argno++] = *additional_args;
   args[argno] = NULL;
   execv (shell, (char **) args);
-  error (1, errno, _("cannot run %s"), shell);
+
+  {
+    int exit_status = (errno == ENOENT ? 127 : 126);
+    error (0, errno, "%s", shell);
+    exit (exit_status);
+  }
 }
 
 /* Return 1 if SHELL is a restricted shell (one not returned by
@@ -407,7 +425,7 @@ restricted_shell (const char *shell)
   return 1;
 }
 
-static void
+void
 usage (int status)
 {
   if (status != 0)
@@ -416,7 +434,7 @@ usage (int status)
   else
     {
       printf (_("Usage: %s [OPTION]... [-] [USER [ARG]...]\n"), program_name);
-      printf (_("\
+      fputs (_("\
 Change the effective user id and group id to that of USER.\n\
 \n\
   -, -l, --login               make the shell a login shell\n\
@@ -425,11 +443,14 @@ Change the effective user id and group id to that of USER.\n\
   -m, --preserve-environment   do not reset environment variables\n\
   -p                           same as -m\n\
   -s, --shell=SHELL            run SHELL if /etc/shells allows it\n\
-      --help                   display this help and exit\n\
-      --version                output version information and exit\n\
+"), stdout);
+      fputs (HELP_OPTION_DESCRIPTION, stdout);
+      fputs (VERSION_OPTION_DESCRIPTION, stdout);
+      fputs (_("\
 \n\
 A mere - implies -l.   If USER not given, assume root.\n\
-"));
+"), stdout);
+      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     }
   exit (status);
 }
@@ -445,17 +466,19 @@ main (int argc, char **argv)
   struct passwd *pw;
   struct passwd pw_copy;
 
+  initialize_main (&argc, &argv);
   program_name = argv[0];
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, LOCALEDIR);
   textdomain (PACKAGE);
 
+  atexit (close_stdout);
+
   fast_startup = 0;
   simulate_login = 0;
   change_environment = 1;
 
-  while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, (int *) 0))
-        != EOF)
+  while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, NULL)) != -1)
     {
       switch (optc)
        {
@@ -483,20 +506,15 @@ main (int argc, char **argv)
          shell = optarg;
          break;
 
+       case_GETOPT_HELP_CHAR;
+
+       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
+
        default:
-         usage (1);
+         usage (EXIT_FAILURE);
        }
     }
 
-  if (show_version)
-    {
-      printf ("su - %s\n", PACKAGE_VERSION);
-      exit (0);
-    }
-
-  if (show_help)
-    usage (0);
-
   if (optind < argc && !strcmp (argv[optind], "-"))
     {
       simulate_login = 1;
@@ -509,9 +527,15 @@ main (int argc, char **argv)
 
   pw = getpwnam (new_user);
   if (pw == 0)
-    error (1, 0, _("user %s does not exist"), new_user);
+    error (EXIT_FAILURE, 0, _("user %s does not exist"), new_user);
   endpwent ();
 
+  /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
+     is a username that is retrieved via NIS (YP), but that doesn't have
+     a default shell listed.  */
+  if (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
+    pw->pw_shell = (char *) DEFAULT_SHELL;
+
   /* Make a copy of the password information and point pw at the local
      copy instead.  Otherwise, some systems (e.g. Linux) would clobber
      the static data through the getlogin call from log_su.  */
@@ -526,7 +550,7 @@ main (int argc, char **argv)
 #ifdef SYSLOG_FAILURE
       log_su (pw, 0);
 #endif
-      error (1, 0, _("incorrect password"));
+      error (EXIT_FAILURE, 0, _("incorrect password"));
     }
 #ifdef SYSLOG_SUCCESS
   else
@@ -535,8 +559,6 @@ main (int argc, char **argv)
     }
 #endif
 
-  if (pw->pw_shell == 0 || pw->pw_shell[0] == 0)
-    pw->pw_shell = (char *) DEFAULT_SHELL;
   if (shell == 0 && change_environment == 0)
     shell = getenv ("SHELL");
   if (shell != 0 && getuid () && restricted_shell (pw->pw_shell))