diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index ae54ce676..a8d26e36e 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,41 @@ +2002-06-12 Christopher Faylor + + * cygheap.cc (cygheap_user::set_name): Set homedrive and homepath to + NULL on user name change. + (cygheap_user::set_logsrv): Allocate enough space for leading \\ so + that we can put this in the environment, if needed. + * cygheap.h (homebodies): New enum. + (cygheap_user::homedrive): New field. + (cygheap_user::homepath): Ditto. + (cygheap_user::env_logsrv): New method. + (cygheap_user::env_homepath): New method. + (cygheap_user::env_homedrive): New method. + (cygheap_user::env_userprofile): New method. + (cygheap_user::ontherange): New method. + * environ.cc (envsize): Eliminate debugging argument. + (environ_init): Assume that envc counts number of elments not total + size. + (spenv): New class. + (spenvs): New array, renamed from forced_winenv_vars, using spenv. + (spenv::retrieve): New method. + (build_env): Rename from 'winenv' -- one stop shopping for building new + environment blocks for both windows and "unix". + * environ.h (build_env: Declare. + (winenv): Delete declaration. + (envsize): Ditto. + * spawn.cc (spawn_guts): Use build_env to build windows and cygwin + environment blocks. + * uinfo.cc (internal_getlogin): Eliminate environment manipulation. + Default to info from GetUserName if it exists. Move HOMEPATH and + HOMEDRIVE stuff elsewhere. Move HOME setting elsewhere. Only set HOME + environment variable in processes that are not parented by a cygwin + process. + (cygheap_user::ontherange): Define new method. + (cygheap_user::env_logsrv): Ditto. + (cygheap_user::env_homepath): Ditto. + (cygheap_user::env_homedrive): Ditto. + (cygheap_user::env_userprofile): Ditto. + 2002-06-11 Christopher Faylor * spawn.cc (spawn_guts): More hToken removal cleanup. diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index d6ce6766b..bd6ae3c65 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -443,6 +443,8 @@ cygheap_user::set_name (const char *new_name) if (pname) cfree (pname); pname = cstrdup (new_name ? new_name : ""); + homedrive = NULL; + homepath = NULL; } void @@ -450,7 +452,13 @@ cygheap_user::set_logsrv (const char *new_logsrv) { if (plogsrv) cfree (plogsrv); - plogsrv = (new_logsrv && *new_logsrv) ? cstrdup (new_logsrv) : NULL; + if (!new_logsrv || !*new_logsrv) + plogsrv = NULL; + else + { + plogsrv = (char *) cmalloc (HEAP_STR, strlen (new_logsrv) + 3) + 2; + strcpy (plogsrv, new_logsrv); + } } void diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index f4ef14459..e4fbe5fcb 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -85,6 +85,14 @@ public: const char *native_path () const { return m->native_path; } }; +enum homebodies +{ + CH_HOMEDRIVE, + CH_HOMEPATH, + CH_HOME +}; + +struct passwd; class cygheap_user { /* Extendend user information. @@ -93,6 +101,8 @@ class cygheap_user char *pname; /* user's name */ char *plogsrv; /* Logon server, may be FQDN */ char *pdomain; /* Logon domain of the user */ + char *homedrive; /* User's home drive */ + char *homepath; /* User's home path */ PSID psid; /* buffer for user's SID */ PSID orig_psid; /* Remains intact even after impersonation */ public: @@ -116,6 +126,11 @@ public: void set_logsrv (const char *new_logsrv); const char *logsrv () const { return plogsrv; } + const char *env_logsrv (); + const char *env_homepath (); + const char *env_homedrive (); + const char *env_userprofile (); + void set_domain (const char *new_domain); const char *domain () const { return pdomain; } @@ -130,6 +145,7 @@ public: set_domain (user.domain ()); set_sid (user.sid ()); } + const char *ontherange (homebodies what, struct passwd * = NULL); }; /* cwd cache stuff. */ diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 8b15c1c08..357af7717 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -195,13 +195,12 @@ getenv (const char *name) return my_findenv (name, &offset); } -extern int __stdcall -envsize (const char * const *in_envp, int debug_print) +static int __stdcall +envsize (const char * const *in_envp) { const char * const *envp; for (envp = in_envp; *envp; envp++) - if (debug_print) - debug_printf ("%s", *envp); + continue; return (1 + envp - in_envp) * sizeof (const char *); } @@ -673,6 +672,8 @@ environ_init (char **envp, int envc) envp_passed_in = 0; else { + envc++; + envc *= sizeof (char *); char **newenv = (char **) malloc (envc); memcpy (newenv, envp, envc); cfree (envp); @@ -749,107 +750,154 @@ env_sort (const void *a, const void *b) return strcmp (*p, *q); } -/* Keep this list in upper case and sorted */ -static const NO_COPY char* forced_winenv_vars [] = - { - "SYSTEMDRIVE", - "SYSTEMROOT", - NULL - }; +struct spenv +{ + const char *name; + const char * (cygheap_user::*from_cygheap) (); + char *retrieve (bool, const char * const = NULL, int = 0); +}; -#define FORCED_WINENV_SIZE (sizeof (forced_winenv_vars) / sizeof (forced_winenv_vars[0])) +/* Keep this list in upper case and sorted */ +static NO_COPY spenv spenvs[] = +{ + {"HOMEPATH=", &cygheap_user::env_homepath}, + {"HOMEDRIVE=", &cygheap_user::env_homedrive}, + {"LOGONSERVER=", &cygheap_user::env_logsrv}, + {"SYSTEMDRIVE=", NULL}, + {"SYSTEMROOT=", NULL}, + {"USERPROFILE=", &cygheap_user::env_userprofile}, +}; + +char * +spenv::retrieve (bool no_envblock, const char *const envname, int len) +{ + if (len && !strncasematch (envname, name, len)) + return NULL; + if (from_cygheap) + { + const char *p; + if (!len) + return NULL; /* No need to force these into the + environment */ + + if (no_envblock) + return cstrdup1 (envname); /* Don't really care what it's set to + if we're calling a cygwin program */ + + /* Make a FOO=BAR entry from the value returned by the cygheap_user + method. */ + p = (cygheap->user.*from_cygheap) (); + int namelen = strlen (name); + char *s = (char *) cmalloc (HEAP_1_STR, namelen + strlen (p) + 1); + strcpy (s, name); + (void) strcpy (s + namelen, p); + return s; + } + + if (len) + return cstrdup1 (envname); + + char dum[1]; + int vallen = GetEnvironmentVariable (name, dum, 0); + if (vallen > 0) + { + int namelen = strlen (name); + char *p = (char *) cmalloc (HEAP_1_STR, namelen + ++vallen); + strcpy (p, name); + if (GetEnvironmentVariable (name, p + namelen, vallen)) + return p; + else + cfree (p); + } + + debug_printf ("warning: %s not present in environment", name); + return NULL; +} + +#define SPENVS_SIZE (sizeof (spenvs) / sizeof (spenvs[0])) /* Create a Windows-style environment block, i.e. a typical character buffer filled with null terminated strings, terminated by double null characters. Converts environment variables noted in conv_envvars into win32 form prior to placing them in the string. */ -char * __stdcall -winenv (const char * const *envp, int keep_posix) +char ** __stdcall +build_env (const char * const *envp, char *&envblock, int &envc, + bool no_envblock) { int len, n, tl; const char * const *srcp; - const char **dstp; - bool saw_forced_winenv[FORCED_WINENV_SIZE] = {0}; - char *p; + char **dstp; + bool saw_spenv[SPENVS_SIZE] = {0}; - debug_printf ("envp %p, keep_posix %d", envp, keep_posix); + debug_printf ("envp %p", envp); tl = 0; for (n = 0; envp[n]; n++) continue; - const char *newenvp[n + 1 + FORCED_WINENV_SIZE]; + char **newenv = (char **) cmalloc (HEAP_1_ARGV, sizeof (char *) * (n + SPENVS_SIZE + 1)); - for (srcp = envp, dstp = newenvp; *srcp; srcp++, dstp++) + for (srcp = envp, dstp = newenv; *srcp; srcp++, dstp++) { - len = strcspn (*srcp, "="); + len = strcspn (*srcp, "=") + 1; + + for (unsigned i = 0; i < SPENVS_SIZE; i++) + if (!saw_spenv[i] && (*dstp = spenvs[i].retrieve (no_envblock,*srcp, len))) + { + saw_spenv[i] = 1; + goto last; + } + win_env *conv; - - if (keep_posix || !(conv = getwinenv (*srcp, *srcp + len + 1))) - *dstp = *srcp; + if (!(conv = getwinenv (*srcp, *srcp + len))) + *dstp = cstrdup1 (*srcp); else - { - p = (char *) alloca (strlen (conv->native) + 1); - strcpy (p, conv->native); - *dstp = p; - } - tl += strlen (*dstp) + 1; - if ((*dstp)[0] == '!' && isdrive ((*dstp) + 1) && (*dstp)[3] == '=') - { - p = (char *) alloca (strlen (*dstp) + 1); - strcpy (p, *dstp); - *p = '='; - *dstp = p; - } + *dstp = cstrdup1 (conv->native); - for (int i = 0; forced_winenv_vars[i]; i++) - if (!saw_forced_winenv[i]) - saw_forced_winenv[i] = strncasematch (forced_winenv_vars[i], *srcp, len); + if ((*dstp)[0] == '!' && isdrive ((*dstp) + 1) && (*dstp)[3] == '=') + **dstp = '='; + + last: + tl += strlen (*dstp) + 1; } - char dum[1]; - for (int i = 0; forced_winenv_vars[i]; i++) - if (!saw_forced_winenv[i]) + for (unsigned i = 0; i < SPENVS_SIZE; i++) + if (!saw_spenv[i]) { - int vallen = GetEnvironmentVariable (forced_winenv_vars[i], dum, 0); - if (vallen > 0) + *dstp = spenvs[i].retrieve (no_envblock); + if (*dstp) { - int namelen = strlen (forced_winenv_vars[i]) + 1; - p = (char *) alloca (namelen + ++vallen); - strcpy (p, forced_winenv_vars[i]); - strcat (p, "="); - if (!GetEnvironmentVariable (forced_winenv_vars[i], p + namelen, - vallen)) - debug_printf ("warning: %s not present in environment", *srcp); - else - { - *dstp++ = p; - tl += strlen (p) + 1; - } + tl += strlen (*dstp) + 1; + dstp++; } } - *dstp = NULL; /* Terminate */ + envc = dstp - newenv; + *dstp = NULL; /* Terminate */ - int envlen = dstp - newenvp; - debug_printf ("env count %d, bytes %d", envlen, tl); - - /* Windows programs expect the environment block to be sorted. */ - qsort (newenvp, envlen, sizeof (char *), env_sort); - - /* Create an environment block suitable for passing to CreateProcess. */ - char *ptr, *envblock; - envblock = (char *) malloc (tl + 2); - for (srcp = newenvp, ptr = envblock; *srcp; srcp++) + if (no_envblock) + envblock = NULL; + else { - len = strlen (*srcp); - memcpy (ptr, *srcp, len + 1); - ptr += len + 1; - } - *ptr = '\0'; /* Two null bytes at the end */ + debug_printf ("env count %d, bytes %d", envc, tl); - return envblock; + /* Windows programs expect the environment block to be sorted. */ + qsort (newenv, envc, sizeof (char *), env_sort); + + /* Create an environment block suitable for passing to CreateProcess. */ + char *ptr; + envblock = (char *) malloc (tl + 2); + for (srcp = newenv, ptr = envblock; *srcp; srcp++) + { + len = strlen (*srcp); + memcpy (ptr, *srcp, len + 1); + ptr += len + 1; + } + *ptr = '\0'; /* Two null bytes at the end */ + } + + return newenv; } /* This idiocy is necessary because the early implementers of cygwin diff --git a/winsup/cygwin/environ.h b/winsup/cygwin/environ.h index 86f70f675..3f372b17c 100644 --- a/winsup/cygwin/environ.h +++ b/winsup/cygwin/environ.h @@ -1,6 +1,6 @@ /* environ.h: Declarations for environ manipulation - Copyright 2000 Red Hat, Inc. + Copyright 2000, 2001, 2002 Red Hat, Inc. This file is part of Cygwin. @@ -12,11 +12,10 @@ details. */ void environ_init (char **, int); /* The structure below is used to control conversion to/from posix-style - * file specs. Currently, only PATH and HOME are converted, but PATH - * needs to use a "convert path list" function while HOME needs a simple - * "convert to posix/win32". For the simple case, where a calculated length - * is required, just return MAX_PATH. *FIXME* - */ + file specs. Currently, only PATH and HOME are converted, but PATH + needs to use a "convert path list" function while HOME needs a simple + "convert to posix/win32". For the simple case, where a calculated length + is required, just return MAX_PATH. *FIXME* */ struct win_env { const char *name; @@ -35,7 +34,7 @@ struct win_env win_env * __stdcall getwinenv (const char *name, const char *posix = NULL); void __stdcall update_envptrs (); -char * __stdcall winenv (const char * const *, int); extern char **__cygwin_environ, ***main_environ; extern "C" char __stdcall **cur_environ (); -int __stdcall envsize (const char * const *, int debug_print = 0); +char ** __stdcall build_env (const char * const *envp, char *&envblock, + int &envc, bool need_envblock); diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 12e82a64d..e5d9bfe86 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -562,18 +562,14 @@ spawn_guts (const char * prog_arg, const char *const *argv, MALLOC_CHECK; } + char *envblock; newargv.all_calloced (); ciresrv.moreinfo->argc = newargv.argc; ciresrv.moreinfo->argv = newargv; - - ciresrv.moreinfo->envc = envsize (envp, 1); - ciresrv.moreinfo->envp = (char **) cmalloc (HEAP_1_ARGV, ciresrv.moreinfo->envc); ciresrv.hexec_proc = hexec_proc; - char **c; - const char * const *e; - for (c = ciresrv.moreinfo->envp, e = envp; *e;) - *c++ = cstrdup1 (*e++); - *c = NULL; + ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc, + real_path.iscygexec ()); + if (mode != _P_OVERLAY || !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc, &ciresrv.moreinfo->myself_pinfo, 0, @@ -605,13 +601,6 @@ spawn_guts (const char * prog_arg, const char *const *argv, flags |= CREATE_SUSPENDED; - /* Build windows style environment list */ - char *envblock; - if (real_path.iscygexec ()) - envblock = NULL; - else - envblock = winenv (envp, 0); - const char *runpath = null_app_name ? NULL : (const char *) real_path; syscall_printf ("null_app_name %d (%s, %.132s)", null_app_name, runpath, one_line.buf); diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 20171c497..9bb0b69b2 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -37,7 +37,7 @@ internal_getlogin (cygheap_user &user) struct passwd *pw = NULL; if (!GetUserName (username, &username_len)) - user.set_name ("unknown"); + user.set_name (NULL); else user.set_name (username); debug_printf ("GetUserName() = %s", user.name ()); @@ -50,7 +50,7 @@ internal_getlogin (cygheap_user &user) user.set_logsrv (NULL); /* First trying to get logon info from environment */ - if ((env = getenv ("USERNAME")) != NULL) + if (!*user.name () && (env = getenv ("USERNAME")) != NULL) user.set_name (env); if ((env = getenv ("USERDOMAIN")) != NULL) user.set_domain (env); @@ -73,58 +73,10 @@ internal_getlogin (cygheap_user &user) } if (!user.logsrv () && user.domain() && get_logon_server(user.domain(), buf, NULL)) - { - user.set_logsrv (buf + 2); - setenv ("LOGONSERVER", buf, 1); - } + user.set_logsrv (buf + 2); debug_printf ("Domain: %s, Logon Server: %s, Windows Username: %s", user.domain (), user.logsrv (), user.name ()); - /* NetUserGetInfo() can be slow in NT domain environment, thus we - * only obtain HOMEDRIVE and HOMEPATH if they are not already set - * in the environment. */ - if (!getenv ("HOMEPATH") || !getenv ("HOMEDRIVE")) - { - LPUSER_INFO_3 ui = NULL; - WCHAR wuser[UNLEN + 1]; - - sys_mbstowcs (wuser, user.name (), sizeof (wuser) / sizeof (*wuser)); - if ((ret = NetUserGetInfo (NULL, wuser, 3, (LPBYTE *)&ui))) - { - if (user.logsrv ()) - { - WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3]; - strcat (strcpy (buf, "\\\\"), user.logsrv ()); - - sys_mbstowcs (wlogsrv, buf, - sizeof (wlogsrv) / sizeof(*wlogsrv)); - ret = NetUserGetInfo (wlogsrv, wuser, 3,(LPBYTE *)&ui); - } - } - if (!ret) - { - sys_wcstombs (buf, ui->usri3_home_dir, MAX_PATH); - if (!buf[0]) - { - sys_wcstombs (buf, ui->usri3_home_dir_drive, MAX_PATH); - if (buf[0]) - strcat (buf, "\\"); - else - { - env = getenv ("SYSTEMDRIVE"); - if (env && *env) - strcat (strcpy (buf, env), "\\"); - else - GetSystemDirectoryA (buf, MAX_PATH); - } - } - setenv ("HOMEPATH", buf + 2, 1); - buf[2] = '\0'; - setenv ("HOMEDRIVE", buf, 1); - } - if (ui) - NetApiBufferFree (ui); - } HANDLE ptok = user.token; /* Which is INVALID_HANDLE_VALUE if no impersonation took place. */ @@ -181,21 +133,13 @@ internal_getlogin (cygheap_user &user) gsid = NO_SID; break; } - if (!strcasematch (user.name (), "SYSTEM") - && user.domain () && user.logsrv ()) - { - if (get_registry_hive_path (user.sid (), buf)) - setenv ("USERPROFILE", buf, 1); - else - unsetenv ("USERPROFILE"); - } } /* If this process is started from a non Cygwin process, set token owner to the same value as token user and primary group to the group which is set as primary group in /etc/passwd. */ - if (ptok != INVALID_HANDLE_VALUE && myself->ppid == 1) + if (ptok != INVALID_HANDLE_VALUE && !myself->ppid_handle) { if (!SetTokenInformation (ptok, TokenOwner, &tu, sizeof tu)) debug_printf ("SetTokenInformation(TokenOwner): %E"); @@ -213,26 +157,11 @@ internal_getlogin (cygheap_user &user) debug_printf ("Cygwins Username: %s", user.name ()); if (!pw) - pw = getpwnam(user.name ()); - if (!getenv ("HOME")) - { - const char *homedrive, *homepath; - if (pw && pw->pw_dir && *pw->pw_dir) - { - setenv ("HOME", pw->pw_dir, 1); - debug_printf ("Set HOME (from /etc/passwd) to %s", pw->pw_dir); - } - else if ((homedrive = getenv ("HOMEDRIVE")) - && (homepath = getenv ("HOMEPATH"))) - { - char home[MAX_PATH]; - strcpy (buf, homedrive); - strcat (buf, homepath); - cygwin_conv_to_full_posix_path (buf, home); - setenv ("HOME", home, 1); - debug_printf ("Set HOME (from HOMEDRIVE/HOMEPATH) to %s", home); - } - } + pw = getpwnam (user.name ()); + + if (!myself->ppid_handle) + (void) cygheap->user.ontherange (CH_HOME, pw); + return pw; } @@ -336,13 +265,151 @@ getegid (void) extern "C" char * cuserid (char *src) { - if (src) + if (!src) + return getlogin (); + + strcpy (src, getlogin ()); + return src; +} + +const char * +cygheap_user::ontherange (homebodies what, struct passwd *pw) +{ + static char buf[MAX_PATH + 1]; + static char homedrive_buf[3]; + LPUSER_INFO_3 ui = NULL; + WCHAR wuser[UNLEN + 1]; + NET_API_STATUS ret; + + if (what == CH_HOME) { - strcpy (src, getlogin ()); - return src; + char *p; + if ((p = getenv ("HOMEDRIVE"))) + { + memcpy (homedrive_buf, p, 2); + homedrive = homedrive_buf; + } + if ((p = getenv ("HOMEPATH"))) + { + strcpy (buf, p); + homepath = buf; + } + if ((p = getenv ("HOME"))) + debug_printf ("HOME is already in the environment %s", p); + else + { + if (!pw) + pw = getpwnam (name ()); + if (pw && pw->pw_dir && *pw->pw_dir) + { + setenv ("HOME", pw->pw_dir, 1); + debug_printf ("Set HOME (from /etc/passwd) to %s", pw->pw_dir); + } + else if (homedrive && homepath) + { + char home[MAX_PATH]; + strcpy (buf, homedrive); + strcat (buf, homepath); + cygwin_conv_to_full_posix_path (buf, home); + setenv ("HOME", home, 1); + debug_printf ("Set HOME (from HOMEDRIVE/HOMEPATH) to %s", home); + } + } } - else + + if (homedrive == NULL) { - return getlogin (); + if (!pw) + pw = getpwnam (name ()); + if (pw && pw->pw_dir && *pw->pw_dir) + cygwin_conv_to_full_win32_path (pw->pw_dir, buf); + else + { + sys_mbstowcs (wuser, name (), sizeof (wuser) / sizeof (*wuser)); + if ((ret = NetUserGetInfo (NULL, wuser, 3, (LPBYTE *)&ui))) + { + if (logsrv ()) + { + WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3]; + strcat (strcpy (buf, "\\\\"), logsrv ()); + sys_mbstowcs (wlogsrv, buf, sizeof (wlogsrv) / sizeof(*wlogsrv)); + ret = NetUserGetInfo (wlogsrv, wuser, 3,(LPBYTE *)&ui); + } + } + if (!ret) + { + char *p; + sys_wcstombs (buf, ui->usri3_home_dir, MAX_PATH); + if (!buf[0]) + { + sys_wcstombs (buf, ui->usri3_home_dir_drive, MAX_PATH); + if (buf[0]) + strcat (buf, "\\"); + else if (!GetSystemDirectory (buf, MAX_PATH)) + strcpy (buf, "c:\\"); + else if ((p = strchr (buf, '\\'))) + p[1] = '\0'; + } + } + if (ui) + NetApiBufferFree (ui); + } + + if (buf[1] != ':') + { + homedrive_buf[0] = homedrive_buf[1] = '\0'; + homepath = buf; + } + else + { + homedrive_buf[0] = buf[0]; + homedrive_buf[1] = buf[1]; + homepath = buf + 2; + } + homedrive = homedrive_buf; + } + + switch (what) + { + case CH_HOMEDRIVE: + return homedrive; + case CH_HOMEPATH: + return homepath; + default: + return homepath; } } + +const char * +cygheap_user::env_logsrv () +{ + char *p = plogsrv - 2; + + *p = p[1] = '\\'; + return p; +} + +const char * +cygheap_user::env_userprofile () +{ + static char buf[512]; /* FIXME: This shouldn't be static. */ + if (strcasematch (name (), "SYSTEM") || !domain () || !logsrv ()) + return NULL; + + if (get_registry_hive_path (sid (), buf)) + return buf; + else + return NULL; +} + +const char * +cygheap_user::env_homepath () +{ + return ontherange (CH_HOMEPATH); +} + +const char * +cygheap_user::env_homedrive () +{ + return ontherange (CH_HOMEDRIVE); +}