2007-01-16 23:24:03 +01:00
|
|
|
diff -rcN coreutils-6.7-orig/getdef.c coreutils-6.7/getdef.c
|
|
|
|
*** coreutils-6.7-orig/getdef.c Thu Jan 1 00:00:00 1970
|
|
|
|
--- coreutils-6.7/getdef.c Tue Jan 16 22:18:41 2007
|
|
|
|
***************
|
|
|
|
*** 0 ****
|
|
|
|
--- 1,257 ----
|
|
|
|
+ /* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
|
|
|
|
+ Author: Thorsten Kukuk <kukuk@suse.de>
|
|
|
|
+
|
|
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
|
|
+ it under the terms of the GNU General Public License version 2 as
|
|
|
|
+ published by the Free Software Foundation.
|
|
|
|
+
|
|
|
|
+ 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. */
|
|
|
|
+
|
|
|
|
+ #ifdef HAVE_CONFIG_H
|
|
|
|
+ #include <config.h>
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
+ #define _GNU_SOURCE
|
|
|
|
+
|
|
|
|
+ #include <errno.h>
|
|
|
|
+ #include <ctype.h>
|
|
|
|
+ #include <stdio.h>
|
|
|
|
+ #include <stdlib.h>
|
|
|
|
+ #include <string.h>
|
|
|
|
+ #include <limits.h>
|
|
|
|
+
|
|
|
|
+ #include "getdef.h"
|
|
|
|
+
|
|
|
|
+ struct item {
|
|
|
|
+ char *name; /* name of the option. */
|
|
|
|
+ char *value; /* value of the option. */
|
|
|
|
+ struct item *next; /* pointer to next option. */
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ static struct item *list = NULL;
|
|
|
|
+
|
|
|
|
+ void
|
|
|
|
+ free_getdef_data (void)
|
|
|
|
+ {
|
|
|
|
+ struct item *ptr;
|
|
|
|
+
|
|
|
|
+ ptr = list;
|
|
|
|
+ while (ptr != NULL)
|
|
|
|
+ {
|
|
|
|
+ struct item *tmp;
|
|
|
|
+ tmp = ptr->next;
|
|
|
|
+ free (ptr->name);
|
|
|
|
+ free (ptr->value);
|
|
|
|
+ free (ptr);
|
|
|
|
+ ptr = tmp;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ list = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Add a new entry to the list. */
|
|
|
|
+ static void
|
|
|
|
+ store (const char *name, const char *value)
|
|
|
|
+ {
|
|
|
|
+ struct item *new = malloc (sizeof (struct item));
|
|
|
|
+
|
|
|
|
+ if (new == NULL)
|
|
|
|
+ abort ();
|
|
|
|
+
|
|
|
|
+ if (name == NULL)
|
|
|
|
+ abort ();
|
|
|
|
+
|
|
|
|
+ new->name = strdup (name);
|
|
|
|
+ new->value = strdup (value?:"");
|
|
|
|
+ new->next = list;
|
|
|
|
+ list = new;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* search a special entry in the list and return the value. */
|
|
|
|
+ static const char *
|
|
|
|
+ search (const char *name)
|
|
|
|
+ {
|
|
|
|
+ struct item *ptr;
|
|
|
|
+
|
|
|
|
+ ptr = list;
|
|
|
|
+ while (ptr != NULL)
|
|
|
|
+ {
|
|
|
|
+ if (strcasecmp (name, ptr->name) == 0)
|
|
|
|
+ return ptr->value;
|
|
|
|
+ ptr = ptr->next;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Load the login.defs file (/etc/login.defs) */
|
|
|
|
+ static void
|
|
|
|
+ load_defaults_internal (const char *filename)
|
|
|
|
+ {
|
|
|
|
+ FILE *fp;
|
|
|
|
+ char *buf = NULL;
|
|
|
|
+ size_t buflen = 0;
|
|
|
|
+
|
|
|
|
+ fp = fopen (filename, "r");
|
|
|
|
+ if (NULL == fp)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ while (!feof (fp))
|
|
|
|
+ {
|
|
|
|
+ char *tmp, *cp;
|
|
|
|
+ #if defined(HAVE_GETLINE)
|
|
|
|
+ ssize_t n = getline (&buf, &buflen, fp);
|
|
|
|
+ #elif defined (HAVE_GETDELIM)
|
|
|
|
+ ssize_t n = getdelim (&buf, &buflen, '\n', fp);
|
|
|
|
+ #else
|
|
|
|
+ ssize_t n;
|
|
|
|
+
|
|
|
|
+ if (buf == NULL)
|
|
|
|
+ {
|
|
|
|
+ buflen = 8096;
|
|
|
|
+ buf = malloc (buflen);
|
|
|
|
+ }
|
|
|
|
+ buf[0] = '\0';
|
|
|
|
+ fgets (buf, buflen - 1, fp);
|
|
|
|
+ if (buf != NULL)
|
|
|
|
+ n = strlen (buf);
|
|
|
|
+ else
|
|
|
|
+ n = 0;
|
|
|
|
+ #endif /* HAVE_GETLINE / HAVE_GETDELIM */
|
|
|
|
+ cp = buf;
|
|
|
|
+
|
|
|
|
+ if (n < 1)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ tmp = strchr (cp, '#'); /* remove comments */
|
|
|
|
+ if (tmp)
|
|
|
|
+ *tmp = '\0';
|
|
|
|
+ while (isspace ((int)*cp)) /* remove spaces and tabs */
|
|
|
|
+ ++cp;
|
|
|
|
+ if (*cp == '\0') /* ignore empty lines */
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (cp[strlen (cp) - 1] == '\n')
|
|
|
|
+ cp[strlen (cp) - 1] = '\0';
|
|
|
|
+
|
|
|
|
+ tmp = strsep (&cp, " \t=");
|
|
|
|
+ if (cp != NULL)
|
|
|
|
+ while (isspace ((int)*cp) || *cp == '=')
|
|
|
|
+ ++cp;
|
|
|
|
+
|
|
|
|
+ store (tmp, cp);
|
|
|
|
+ }
|
|
|
|
+ fclose (fp);
|
|
|
|
+
|
|
|
|
+ if (buf)
|
|
|
|
+ free (buf);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static void
|
|
|
|
+ load_defaults (void)
|
|
|
|
+ {
|
|
|
|
+ load_defaults_internal ("/etc/default/su");
|
|
|
|
+ load_defaults_internal ("/etc/login.defs");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int
|
|
|
|
+ getdef_bool (const char *name, int dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *val;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ val = search (name);
|
|
|
|
+
|
|
|
|
+ if (val == NULL)
|
|
|
|
+ return dflt;
|
|
|
|
+
|
|
|
|
+ return (strcasecmp (val, "yes") == 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ long
|
|
|
|
+ getdef_num (const char *name, long dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *val;
|
|
|
|
+ char *cp;
|
|
|
|
+ long retval;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ val = search (name);
|
|
|
|
+
|
|
|
|
+ if (val == NULL)
|
|
|
|
+ return dflt;
|
|
|
|
+
|
|
|
|
+ retval = strtol (val, &cp, 0);
|
|
|
|
+ if (*cp != '\0' ||
|
|
|
|
+ ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE))
|
|
|
|
+ {
|
|
|
|
+ fprintf (stderr,
|
|
|
|
+ "%s contains invalid numerical value: %s!\n",
|
|
|
|
+ name, val);
|
|
|
|
+ retval = dflt;
|
|
|
|
+ }
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ unsigned long
|
|
|
|
+ getdef_unum (const char *name, unsigned long dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *val;
|
|
|
|
+ char *cp;
|
|
|
|
+ unsigned long retval;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ val = search (name);
|
|
|
|
+
|
|
|
|
+ if (val == NULL)
|
|
|
|
+ return dflt;
|
|
|
|
+
|
|
|
|
+ retval = strtoul (val, &cp, 0);
|
|
|
|
+ if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE))
|
|
|
|
+ {
|
|
|
|
+ fprintf (stderr,
|
|
|
|
+ "%s contains invalid numerical value: %s!\n",
|
|
|
|
+ name, val);
|
|
|
|
+ retval = dflt;
|
|
|
|
+ }
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const char *
|
|
|
|
+ getdef_str (const char *name, const char *dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *retval;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ retval = search (name);
|
|
|
|
+
|
|
|
|
+ return retval ?: dflt;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #if defined(TEST)
|
|
|
|
+
|
|
|
|
+ int
|
|
|
|
+ main ()
|
|
|
|
+ {
|
|
|
|
+ printf ("CYPT=%s\n", getdef_str ("cRypt", "no"));
|
|
|
|
+ printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab",""));
|
|
|
|
+ printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes"));
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endif
|
|
|
|
diff -rcN coreutils-6.7-orig/getdef.h coreutils-6.7/getdef.h
|
|
|
|
*** coreutils-6.7-orig/getdef.h Thu Jan 1 00:00:00 1970
|
|
|
|
--- coreutils-6.7/getdef.h Tue Jan 16 22:18:41 2007
|
|
|
|
***************
|
|
|
|
*** 0 ****
|
|
|
|
--- 1,29 ----
|
|
|
|
+ /* Copyright (C) 2003, 2005 Thorsten Kukuk
|
|
|
|
+ Author: Thorsten Kukuk <kukuk@suse.de>
|
|
|
|
+
|
|
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
|
|
+ it under the terms of the GNU General Public License version 2 as
|
|
|
|
+ published by the Free Software Foundation.
|
|
|
|
+
|
|
|
|
+ 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. */
|
|
|
|
+
|
|
|
|
+ #ifndef _GETDEF_H_
|
|
|
|
+
|
|
|
|
+ #define _GETDEF_H_ 1
|
|
|
|
+
|
|
|
|
+ extern int getdef_bool (const char *name, int dflt);
|
|
|
|
+ extern long getdef_num (const char *name, long dflt);
|
|
|
|
+ extern unsigned long getdef_unum (const char *name, unsigned long dflt);
|
|
|
|
+ extern const char *getdef_str (const char *name, const char *dflt);
|
|
|
|
+
|
|
|
|
+ /* Free all data allocated by getdef_* calls before. */
|
|
|
|
+ extern void free_getdef_data (void);
|
|
|
|
+
|
|
|
|
+ #endif /* _GETDEF_H_ */
|
|
|
|
diff -rcN coreutils-6.7-orig/src/getdef.c coreutils-6.7/src/getdef.c
|
|
|
|
*** coreutils-6.7-orig/src/getdef.c Thu Jan 1 00:00:00 1970
|
|
|
|
--- coreutils-6.7/src/getdef.c Tue Jan 16 22:18:57 2007
|
|
|
|
***************
|
|
|
|
*** 0 ****
|
|
|
|
--- 1,257 ----
|
|
|
|
+ /* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
|
|
|
|
+ Author: Thorsten Kukuk <kukuk@suse.de>
|
|
|
|
+
|
|
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
|
|
+ it under the terms of the GNU General Public License version 2 as
|
|
|
|
+ published by the Free Software Foundation.
|
|
|
|
+
|
|
|
|
+ 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. */
|
|
|
|
+
|
|
|
|
+ #ifdef HAVE_CONFIG_H
|
|
|
|
+ #include <config.h>
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
+ #define _GNU_SOURCE
|
|
|
|
+
|
|
|
|
+ #include <errno.h>
|
|
|
|
+ #include <ctype.h>
|
|
|
|
+ #include <stdio.h>
|
|
|
|
+ #include <stdlib.h>
|
|
|
|
+ #include <string.h>
|
|
|
|
+ #include <limits.h>
|
|
|
|
+
|
|
|
|
+ #include "getdef.h"
|
|
|
|
+
|
|
|
|
+ struct item {
|
|
|
|
+ char *name; /* name of the option. */
|
|
|
|
+ char *value; /* value of the option. */
|
|
|
|
+ struct item *next; /* pointer to next option. */
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ static struct item *list = NULL;
|
|
|
|
+
|
|
|
|
+ void
|
|
|
|
+ free_getdef_data (void)
|
|
|
|
+ {
|
|
|
|
+ struct item *ptr;
|
|
|
|
+
|
|
|
|
+ ptr = list;
|
|
|
|
+ while (ptr != NULL)
|
|
|
|
+ {
|
|
|
|
+ struct item *tmp;
|
|
|
|
+ tmp = ptr->next;
|
|
|
|
+ free (ptr->name);
|
|
|
|
+ free (ptr->value);
|
|
|
|
+ free (ptr);
|
|
|
|
+ ptr = tmp;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ list = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Add a new entry to the list. */
|
|
|
|
+ static void
|
|
|
|
+ store (const char *name, const char *value)
|
|
|
|
+ {
|
|
|
|
+ struct item *new = malloc (sizeof (struct item));
|
|
|
|
+
|
|
|
|
+ if (new == NULL)
|
|
|
|
+ abort ();
|
|
|
|
+
|
|
|
|
+ if (name == NULL)
|
|
|
|
+ abort ();
|
|
|
|
+
|
|
|
|
+ new->name = strdup (name);
|
|
|
|
+ new->value = strdup (value?:"");
|
|
|
|
+ new->next = list;
|
|
|
|
+ list = new;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* search a special entry in the list and return the value. */
|
|
|
|
+ static const char *
|
|
|
|
+ search (const char *name)
|
|
|
|
+ {
|
|
|
|
+ struct item *ptr;
|
|
|
|
+
|
|
|
|
+ ptr = list;
|
|
|
|
+ while (ptr != NULL)
|
|
|
|
+ {
|
|
|
|
+ if (strcasecmp (name, ptr->name) == 0)
|
|
|
|
+ return ptr->value;
|
|
|
|
+ ptr = ptr->next;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Load the login.defs file (/etc/login.defs) */
|
|
|
|
+ static void
|
|
|
|
+ load_defaults_internal (const char *filename)
|
|
|
|
+ {
|
|
|
|
+ FILE *fp;
|
|
|
|
+ char *buf = NULL;
|
|
|
|
+ size_t buflen = 0;
|
|
|
|
+
|
|
|
|
+ fp = fopen (filename, "r");
|
|
|
|
+ if (NULL == fp)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ while (!feof (fp))
|
|
|
|
+ {
|
|
|
|
+ char *tmp, *cp;
|
|
|
|
+ #if defined(HAVE_GETLINE)
|
|
|
|
+ ssize_t n = getline (&buf, &buflen, fp);
|
|
|
|
+ #elif defined (HAVE_GETDELIM)
|
|
|
|
+ ssize_t n = getdelim (&buf, &buflen, '\n', fp);
|
|
|
|
+ #else
|
|
|
|
+ ssize_t n;
|
|
|
|
+
|
|
|
|
+ if (buf == NULL)
|
|
|
|
+ {
|
|
|
|
+ buflen = 8096;
|
|
|
|
+ buf = malloc (buflen);
|
|
|
|
+ }
|
|
|
|
+ buf[0] = '\0';
|
|
|
|
+ fgets (buf, buflen - 1, fp);
|
|
|
|
+ if (buf != NULL)
|
|
|
|
+ n = strlen (buf);
|
|
|
|
+ else
|
|
|
|
+ n = 0;
|
|
|
|
+ #endif /* HAVE_GETLINE / HAVE_GETDELIM */
|
|
|
|
+ cp = buf;
|
|
|
|
+
|
|
|
|
+ if (n < 1)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ tmp = strchr (cp, '#'); /* remove comments */
|
|
|
|
+ if (tmp)
|
|
|
|
+ *tmp = '\0';
|
|
|
|
+ while (isspace ((int)*cp)) /* remove spaces and tabs */
|
|
|
|
+ ++cp;
|
|
|
|
+ if (*cp == '\0') /* ignore empty lines */
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (cp[strlen (cp) - 1] == '\n')
|
|
|
|
+ cp[strlen (cp) - 1] = '\0';
|
|
|
|
+
|
|
|
|
+ tmp = strsep (&cp, " \t=");
|
|
|
|
+ if (cp != NULL)
|
|
|
|
+ while (isspace ((int)*cp) || *cp == '=')
|
|
|
|
+ ++cp;
|
|
|
|
+
|
|
|
|
+ store (tmp, cp);
|
|
|
|
+ }
|
|
|
|
+ fclose (fp);
|
|
|
|
+
|
|
|
|
+ if (buf)
|
|
|
|
+ free (buf);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static void
|
|
|
|
+ load_defaults (void)
|
|
|
|
+ {
|
|
|
|
+ load_defaults_internal ("/etc/default/su");
|
|
|
|
+ load_defaults_internal ("/etc/login.defs");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int
|
|
|
|
+ getdef_bool (const char *name, int dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *val;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ val = search (name);
|
|
|
|
+
|
|
|
|
+ if (val == NULL)
|
|
|
|
+ return dflt;
|
|
|
|
+
|
|
|
|
+ return (strcasecmp (val, "yes") == 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ long
|
|
|
|
+ getdef_num (const char *name, long dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *val;
|
|
|
|
+ char *cp;
|
|
|
|
+ long retval;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ val = search (name);
|
|
|
|
+
|
|
|
|
+ if (val == NULL)
|
|
|
|
+ return dflt;
|
|
|
|
+
|
|
|
|
+ retval = strtol (val, &cp, 0);
|
|
|
|
+ if (*cp != '\0' ||
|
|
|
|
+ ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE))
|
|
|
|
+ {
|
|
|
|
+ fprintf (stderr,
|
|
|
|
+ "%s contains invalid numerical value: %s!\n",
|
|
|
|
+ name, val);
|
|
|
|
+ retval = dflt;
|
|
|
|
+ }
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ unsigned long
|
|
|
|
+ getdef_unum (const char *name, unsigned long dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *val;
|
|
|
|
+ char *cp;
|
|
|
|
+ unsigned long retval;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ val = search (name);
|
|
|
|
+
|
|
|
|
+ if (val == NULL)
|
|
|
|
+ return dflt;
|
|
|
|
+
|
|
|
|
+ retval = strtoul (val, &cp, 0);
|
|
|
|
+ if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE))
|
|
|
|
+ {
|
|
|
|
+ fprintf (stderr,
|
|
|
|
+ "%s contains invalid numerical value: %s!\n",
|
|
|
|
+ name, val);
|
|
|
|
+ retval = dflt;
|
|
|
|
+ }
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const char *
|
|
|
|
+ getdef_str (const char *name, const char *dflt)
|
|
|
|
+ {
|
|
|
|
+ const char *retval;
|
|
|
|
+
|
|
|
|
+ if (list == NULL)
|
|
|
|
+ load_defaults ();
|
|
|
|
+
|
|
|
|
+ retval = search (name);
|
|
|
|
+
|
|
|
|
+ return retval ?: dflt;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #if defined(TEST)
|
|
|
|
+
|
|
|
|
+ int
|
|
|
|
+ main ()
|
|
|
|
+ {
|
|
|
|
+ printf ("CYPT=%s\n", getdef_str ("cRypt", "no"));
|
|
|
|
+ printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab",""));
|
|
|
|
+ printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes"));
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endif
|
|
|
|
diff -rcN coreutils-6.7-orig/src/getdef.h coreutils-6.7/src/getdef.h
|
|
|
|
*** coreutils-6.7-orig/src/getdef.h Thu Jan 1 00:00:00 1970
|
|
|
|
--- coreutils-6.7/src/getdef.h Tue Jan 16 22:18:57 2007
|
|
|
|
***************
|
|
|
|
*** 0 ****
|
|
|
|
--- 1,29 ----
|
|
|
|
+ /* Copyright (C) 2003, 2005 Thorsten Kukuk
|
|
|
|
+ Author: Thorsten Kukuk <kukuk@suse.de>
|
|
|
|
+
|
|
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
|
|
+ it under the terms of the GNU General Public License version 2 as
|
|
|
|
+ published by the Free Software Foundation.
|
|
|
|
+
|
|
|
|
+ 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. */
|
|
|
|
+
|
|
|
|
+ #ifndef _GETDEF_H_
|
|
|
|
+
|
|
|
|
+ #define _GETDEF_H_ 1
|
|
|
|
+
|
|
|
|
+ extern int getdef_bool (const char *name, int dflt);
|
|
|
|
+ extern long getdef_num (const char *name, long dflt);
|
|
|
|
+ extern unsigned long getdef_unum (const char *name, unsigned long dflt);
|
|
|
|
+ extern const char *getdef_str (const char *name, const char *dflt);
|
|
|
|
+
|
|
|
|
+ /* Free all data allocated by getdef_* calls before. */
|
|
|
|
+ extern void free_getdef_data (void);
|
|
|
|
+
|
|
|
|
+ #endif /* _GETDEF_H_ */
|
|
|
|
diff -rcN coreutils-6.7-orig/src/su.c coreutils-6.7/src/su.c
|
|
|
|
*** coreutils-6.7-orig/src/su.c Sun Oct 22 16:54:15 2006
|
|
|
|
--- coreutils-6.7/src/su.c Tue Jan 16 22:19:02 2007
|
|
|
|
***************
|
|
|
|
*** 38,43 ****
|
|
|
|
--- 38,49 ----
|
|
|
|
restricts who can su to UID 0 accounts. RMS considers that to
|
|
|
|
be fascist.
|
|
|
|
|
|
|
|
+ Actually, with PAM, su has nothing to do with whether or not a
|
|
|
|
+ wheel group is enforced by su. RMS tries to restrict your access
|
|
|
|
+ to a su which implements the wheel group, but PAM considers that
|
|
|
|
+ to be fascist, and gives the user/sysadmin the opportunity to
|
|
|
|
+ enforce a wheel group by proper editing of /etc/pam.d/su
|
|
|
|
+
|
|
|
|
Compile-time options:
|
|
|
|
-DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog.
|
|
|
|
-DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog.
|
|
|
|
***************
|
|
|
|
*** 53,58 ****
|
|
|
|
--- 59,71 ----
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+ #include <security/pam_appl.h>
|
|
|
|
+ #include <security/pam_misc.h>
|
|
|
|
+ #include <signal.h>
|
|
|
|
+ #include <sys/wait.h>
|
|
|
|
+ #include <sys/fsuid.h>
|
|
|
|
+ #endif
|
|
|
|
|
|
|
|
/* Hide any system prototype for getusershell.
|
|
|
|
This is necessary because some Cray systems have a conflicting
|
|
|
|
***************
|
|
|
|
*** 66,71 ****
|
|
|
|
--- 79,87 ----
|
|
|
|
|
|
|
|
#if HAVE_SYSLOG_H && HAVE_SYSLOG
|
|
|
|
# include <syslog.h>
|
|
|
|
+ # define SYSLOG_SUCCESS 1
|
|
|
|
+ # define SYSLOG_FAILURE 1
|
|
|
|
+ # define SYSLOG_NON_ROOT 1
|
|
|
|
#else
|
|
|
|
# undef SYSLOG_SUCCESS
|
|
|
|
# undef SYSLOG_FAILURE
|
|
|
|
***************
|
|
|
|
*** 99,117 ****
|
|
|
|
# include <paths.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The default PATH for simulated logins to non-superuser accounts. */
|
|
|
|
! #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. */
|
|
|
|
! #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"
|
|
|
|
--- 115,127 ----
|
|
|
|
# include <paths.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
+ #include "getdef.h"
|
|
|
|
+
|
|
|
|
/* The default PATH for simulated logins to non-superuser accounts. */
|
|
|
|
! #define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin"
|
|
|
|
|
|
|
|
/* The default PATH for simulated logins to superuser accounts. */
|
|
|
|
! #define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin:/usr/X11R6/bin"
|
|
|
|
|
|
|
|
/* The shell to run if none is given in the user's passwd entry. */
|
|
|
|
#define DEFAULT_SHELL "/bin/sh"
|
|
|
|
***************
|
|
|
|
*** 119,125 ****
|
|
|
|
--- 129,137 ----
|
|
|
|
/* The user to become if none is specified. */
|
|
|
|
#define DEFAULT_USER "root"
|
|
|
|
|
|
|
|
+ #ifndef USE_PAM
|
|
|
|
char *crypt ();
|
|
|
|
+ #endif
|
|
|
|
char *getusershell ();
|
|
|
|
void endusershell ();
|
|
|
|
void setusershell ();
|
|
|
|
***************
|
|
|
|
*** 216,222 ****
|
|
|
|
--- 228,253 ----
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+
|
|
|
|
+ static pam_handle_t *pamh = NULL;
|
|
|
|
+ static int retval;
|
|
|
|
+ static struct pam_conv conv =
|
|
|
|
+ {
|
|
|
|
+ misc_conv,
|
|
|
|
+ NULL
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ #define PAM_BAIL_P(a) \
|
|
|
|
+ if (retval) \
|
|
|
|
+ { \
|
|
|
|
+ pam_end (pamh, PAM_SUCCESS); \
|
|
|
|
+ a; \
|
|
|
|
+ }
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
/* Ask the user for a password.
|
|
|
|
+ If PAM is in use, let PAM ask for the password if necessary.
|
|
|
|
Return true if the user gives the correct password for entry PW,
|
|
|
|
false if not. Return true without asking for a password if run by UID 0
|
|
|
|
or if PW has an empty password. */
|
|
|
|
***************
|
|
|
|
*** 224,233 ****
|
|
|
|
static bool
|
|
|
|
correct_password (const struct passwd *pw)
|
|
|
|
{
|
|
|
|
char *unencrypted, *encrypted, *correct;
|
|
|
|
#if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
|
|
|
|
/* Shadow passwd stuff for SVR3 and maybe other systems. */
|
|
|
|
! struct spwd *sp = getspnam (pw->pw_name);
|
|
|
|
|
|
|
|
endspent ();
|
|
|
|
if (sp)
|
|
|
|
--- 255,303 ----
|
|
|
|
static bool
|
|
|
|
correct_password (const struct passwd *pw)
|
|
|
|
{
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+ const struct passwd *lpw;
|
|
|
|
+ const char *cp;
|
|
|
|
+
|
|
|
|
+ retval = pam_start ("su", pw->pw_name, &conv, &pamh);
|
|
|
|
+ PAM_BAIL_P (return false);
|
|
|
|
+
|
|
|
|
+ if (isatty (0) && (cp = ttyname (0)) != NULL)
|
|
|
|
+ {
|
|
|
|
+ const char *tty;
|
|
|
|
+
|
|
|
|
+ if (strncmp (cp, "/dev/", 5) == 0)
|
|
|
|
+ tty = cp + 5;
|
|
|
|
+ else
|
|
|
|
+ tty = cp;
|
|
|
|
+ retval = pam_set_item (pamh, PAM_TTY, tty);
|
|
|
|
+ PAM_BAIL_P (return false);
|
|
|
|
+ }
|
|
|
|
+ cp = getlogin ();
|
|
|
|
+ if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
|
|
|
|
+ lpw = getpwuid (getuid ());
|
|
|
|
+ if (lpw)
|
|
|
|
+ {
|
|
|
|
+ retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
|
|
|
|
+ PAM_BAIL_P (return false);
|
|
|
|
+ }
|
|
|
|
+ retval = pam_authenticate (pamh, 0);
|
|
|
|
+ PAM_BAIL_P (return false);
|
|
|
|
+ retval = pam_acct_mgmt (pamh, 0);
|
|
|
|
+ if (retval == PAM_NEW_AUTHTOK_REQD)
|
|
|
|
+ {
|
|
|
|
+ /* password has expired. Offer option to change it. */
|
|
|
|
+ retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
|
|
|
|
+ PAM_BAIL_P (return false);
|
|
|
|
+ }
|
|
|
|
+ PAM_BAIL_P (return false);
|
|
|
|
+ /* must be authenticated if this point was reached */
|
|
|
|
+ return true;
|
|
|
|
+ #else /* !USE_PAM */
|
|
|
|
char *unencrypted, *encrypted, *correct;
|
|
|
|
#if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
|
|
|
|
/* Shadow passwd stuff for SVR3 and maybe other systems. */
|
|
|
|
! const struct spwd *sp = getspnam (pw->pw_name);
|
|
|
|
|
|
|
|
endspent ();
|
|
|
|
if (sp)
|
|
|
|
***************
|
|
|
|
*** 248,253 ****
|
|
|
|
--- 318,324 ----
|
|
|
|
encrypted = crypt (unencrypted, correct);
|
|
|
|
memset (unencrypted, 0, strlen (unencrypted));
|
|
|
|
return STREQ (encrypted, correct);
|
|
|
|
+ #endif /* !USE_PAM */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update `environ' for the new shell based on PW, with SHELL being
|
|
|
|
***************
|
|
|
|
*** 272,279 ****
|
|
|
|
xsetenv ("USER", pw->pw_name);
|
|
|
|
xsetenv ("LOGNAME", pw->pw_name);
|
|
|
|
xsetenv ("PATH", (pw->pw_uid
|
|
|
|
! ? DEFAULT_LOGIN_PATH
|
|
|
|
! : DEFAULT_ROOT_LOGIN_PATH));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
--- 343,350 ----
|
|
|
|
xsetenv ("USER", pw->pw_name);
|
|
|
|
xsetenv ("LOGNAME", pw->pw_name);
|
|
|
|
xsetenv ("PATH", (pw->pw_uid
|
|
|
|
! ? getdef_str ("PATH", DEFAULT_LOGIN_PATH)
|
|
|
|
! : getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
***************
|
|
|
|
*** 283,288 ****
|
|
|
|
--- 354,365 ----
|
|
|
|
{
|
|
|
|
xsetenv ("HOME", pw->pw_dir);
|
|
|
|
xsetenv ("SHELL", shell);
|
|
|
|
+ if (getdef_bool ("ALWAYS_SET_PATH", 0))
|
|
|
|
+ xsetenv ("PATH", (pw->pw_uid
|
|
|
|
+ ? getdef_str ("PATH",
|
|
|
|
+ DEFAULT_LOGIN_PATH)
|
|
|
|
+ : getdef_str ("SUPATH",
|
|
|
|
+ DEFAULT_ROOT_LOGIN_PATH)));
|
|
|
|
if (pw->pw_uid)
|
|
|
|
{
|
|
|
|
xsetenv ("USER", pw->pw_name);
|
|
|
|
***************
|
|
|
|
*** 303,314 ****
|
|
|
|
--- 380,414 ----
|
|
|
|
error (EXIT_FAIL, errno, _("cannot set groups"));
|
|
|
|
endgrent ();
|
|
|
|
#endif
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+ retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
|
|
|
|
+ if (retval != PAM_SUCCESS)
|
|
|
|
+ error (EXIT_FAIL, 0, "%s", pam_strerror (pamh, retval));
|
|
|
|
+
|
|
|
|
+ retval = pam_open_session (pamh,0);
|
|
|
|
+ if (retval != PAM_SUCCESS)
|
|
|
|
+ {
|
|
|
|
+ pam_setcred (pamh, PAM_DELETE_CRED);
|
|
|
|
+ error (EXIT_FAIL, 0, "could not open session: %s",
|
|
|
|
+ pam_strerror (pamh, retval));
|
|
|
|
+ }
|
|
|
|
+ #endif /* USE_PAM */
|
|
|
|
if (setgid (pw->pw_gid))
|
|
|
|
error (EXIT_FAIL, errno, _("cannot set group id"));
|
|
|
|
if (setuid (pw->pw_uid))
|
|
|
|
error (EXIT_FAIL, errno, _("cannot set user id"));
|
|
|
|
}
|
|
|
|
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+ static bool caught = false;
|
|
|
|
+ /* Signal handler for parent process later */
|
|
|
|
+ static void
|
|
|
|
+ su_catch_sig (int sig)
|
|
|
|
+ {
|
|
|
|
+ caught = true;
|
|
|
|
+ }
|
|
|
|
+ #endif
|
|
|
|
+
|
|
|
|
/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
|
|
|
|
If COMMAND is nonzero, pass it to the shell with the -c option.
|
|
|
|
Pass ADDITIONAL_ARGS to the shell as more arguments; there
|
|
|
|
***************
|
|
|
|
*** 321,326 ****
|
|
|
|
--- 421,523 ----
|
|
|
|
size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
|
|
|
|
char const **args = xnmalloc (n_args, sizeof *args);
|
|
|
|
size_t argno = 1;
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+ pid_t child;
|
|
|
|
+ sigset_t ourset;
|
|
|
|
+ int status;
|
|
|
|
+
|
|
|
|
+ child = fork ();
|
|
|
|
+ if (child == (pid_t) -1)
|
|
|
|
+ error (EXIT_FAILURE, errno, "cannot fork");
|
|
|
|
+
|
|
|
|
+ if (child != 0)
|
|
|
|
+ {
|
|
|
|
+ /* parent only */
|
|
|
|
+ sigfillset (&ourset);
|
|
|
|
+ if (sigprocmask (SIG_BLOCK, &ourset, NULL))
|
|
|
|
+ {
|
|
|
|
+ error (0, errno, "cannot block signals");
|
|
|
|
+ caught = true;
|
|
|
|
+ }
|
|
|
|
+ if (!caught)
|
|
|
|
+ {
|
|
|
|
+ struct sigaction action;
|
|
|
|
+ action.sa_handler = su_catch_sig;
|
|
|
|
+ sigemptyset (&action.sa_mask);
|
|
|
|
+ action.sa_flags = 0;
|
|
|
|
+ sigemptyset (&ourset);
|
|
|
|
+ if (sigaddset (&ourset, SIGTERM)
|
|
|
|
+ || sigaddset (&ourset, SIGALRM)
|
|
|
|
+ || sigaction (SIGTERM, &action, NULL)
|
|
|
|
+ || sigprocmask (SIG_UNBLOCK, &ourset, NULL))
|
|
|
|
+ {
|
|
|
|
+ error (0, errno, "cannot set signal handler");
|
|
|
|
+ caught = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!caught)
|
|
|
|
+ {
|
|
|
|
+ for (;;)
|
|
|
|
+ {
|
|
|
|
+ pid_t pid;
|
|
|
|
+
|
|
|
|
+ pid = waitpid (child, &status, WUNTRACED);
|
|
|
|
+
|
|
|
|
+ if (WIFSTOPPED (status))
|
|
|
|
+ {
|
|
|
|
+ kill (getpid (), SIGSTOP);
|
|
|
|
+ /* once we get here, we must have resumed */
|
|
|
|
+ kill (pid, SIGCONT);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (WIFSIGNALED (status))
|
|
|
|
+ status = WTERMSIG (status) + 128;
|
|
|
|
+ else
|
|
|
|
+ status = WEXITSTATUS (status);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ status = 1;
|
|
|
|
+
|
|
|
|
+ if (caught)
|
|
|
|
+ {
|
|
|
|
+ fprintf (stderr, "\nSession terminated, killing shell...");
|
|
|
|
+ kill (child, SIGTERM);
|
|
|
|
+ }
|
|
|
|
+ retval = pam_setcred (pamh, PAM_DELETE_CRED);
|
|
|
|
+ PAM_BAIL_P (exit (EXIT_FAILURE));
|
|
|
|
+ retval = pam_close_session (pamh, 0);
|
|
|
|
+ PAM_BAIL_P (exit (EXIT_FAILURE));
|
|
|
|
+ retval = pam_end (pamh, PAM_SUCCESS);
|
|
|
|
+ PAM_BAIL_P (exit (EXIT_FAILURE));
|
|
|
|
+ if (caught)
|
|
|
|
+ {
|
|
|
|
+ sleep (2);
|
|
|
|
+ kill (child, SIGKILL);
|
|
|
|
+ fprintf (stderr, " ...killed.\n");
|
|
|
|
+ }
|
|
|
|
+ exit (status);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* child shell */
|
|
|
|
+
|
|
|
|
+ /* Export env variables declared by PAM modules */
|
|
|
|
+ {
|
|
|
|
+ const char *const *env;
|
|
|
|
+
|
|
|
|
+ env = (const char *const *) pam_getenvlist (pamh);
|
|
|
|
+ while (env && *env)
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ if (putenv (*env) != 0)
|
|
|
|
+ xalloc_die ();
|
|
|
|
+ env++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pam_end (pamh, 0);
|
|
|
|
+ #endif
|
|
|
|
|
|
|
|
if (simulate_login)
|
|
|
|
{
|
|
|
|
***************
|
|
|
|
*** 339,344 ****
|
|
|
|
--- 536,546 ----
|
|
|
|
args[argno++] = "-f";
|
|
|
|
if (command)
|
|
|
|
{
|
|
|
|
+ if (simulate_login)
|
|
|
|
+ /* Bash 2.0 have to be invoked as `-su'. See the comments in
|
|
|
|
+ `shell.c (run_startup_files)'. */
|
|
|
|
+ args[0] = "-su";
|
|
|
|
+
|
|
|
|
args[argno++] = "-c";
|
|
|
|
args[argno++] = command;
|
|
|
|
}
|
|
|
|
***************
|
|
|
|
*** 495,500 ****
|
|
|
|
--- 697,705 ----
|
|
|
|
#ifdef SYSLOG_FAILURE
|
|
|
|
log_su (pw, false);
|
|
|
|
#endif
|
|
|
|
+ #ifdef USE_PAM
|
|
|
|
+ sleep (getdef_num ("FAIL_DELAY", 1));
|
|
|
|
+ #endif
|
|
|
|
error (EXIT_FAIL, 0, _("incorrect password"));
|
|
|
|
}
|
|
|
|
#ifdef SYSLOG_SUCCESS
|
|
|
|
***************
|
|
|
|
*** 516,524 ****
|
|
|
|
shell = NULL;
|
|
|
|
}
|
|
|
|
shell = xstrdup (shell ? shell : pw->pw_shell);
|
|
|
|
modify_environment (pw, shell);
|
|
|
|
|
|
|
|
- change_identity (pw);
|
|
|
|
if (simulate_login && chdir (pw->pw_dir) != 0)
|
|
|
|
error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
|
|
|
|
|
|
|
|
--- 721,732 ----
|
|
|
|
shell = NULL;
|
|
|
|
}
|
|
|
|
shell = xstrdup (shell ? shell : pw->pw_shell);
|
|
|
|
+ change_identity (pw);
|
|
|
|
+
|
|
|
|
+ /* Set environment after pam_open_session, which may put KRB5CCNAME
|
|
|
|
+ into the pam_env, etc. */
|
|
|
|
modify_environment (pw, shell);
|
|
|
|
|
|
|
|
if (simulate_login && chdir (pw->pw_dir) != 0)
|
|
|
|
error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
|
|
|
|
|