Cygwin: newgrp: first full version
- add '-' option - make group argument optional - drop ability to take a numerical group argument - simplify usage output to bare minimum - Add manpage and documentation Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
73e4ded2fe
commit
8bd56ec873
|
@ -1939,6 +1939,56 @@ D: on /d type fat (binary,user,noumount)
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
|
||||||
|
<refentry id="newgrp">
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>newgrp</refentrytitle>
|
||||||
|
<manvolnum>1</manvolnum>
|
||||||
|
<refmiscinfo class="manual">Cygwin Utilities</refmiscinfo>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>newgrp</refname>
|
||||||
|
<refpurpose>change primary group for a command</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<cmdsynopsis>
|
||||||
|
<command>newgrp</command>
|
||||||
|
<arg choice="opt">-</arg>
|
||||||
|
<arg choice="opt"><replaceable>group</replaceable></arg>
|
||||||
|
<arg><replaceable>command</replaceable>
|
||||||
|
<arg rep="repeat"><replaceable>args</replaceable></arg>
|
||||||
|
</arg>
|
||||||
|
</cmdsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1 id="newgrp-desc">
|
||||||
|
<title>Description</title>
|
||||||
|
<para><command>newgrp</command> changes the primary group for a
|
||||||
|
command.</para>
|
||||||
|
|
||||||
|
<para>If the '-' flag is given as first argument, the user's environment
|
||||||
|
will be reinitialized as though the user had logged in, otherwise the
|
||||||
|
current environment, including current working directory, remains
|
||||||
|
unchanged.</para>
|
||||||
|
|
||||||
|
<para><command>newgrp</command> changes the current primary group to the
|
||||||
|
named group, or to the default group listed in /etc/passwd if no group
|
||||||
|
name is given.</para>
|
||||||
|
|
||||||
|
<para>By default, the user's standard shell is started, called as login
|
||||||
|
shell if the '-' flag has been specified. If a group has been given
|
||||||
|
as argument, a command and its arguments can be specified on the
|
||||||
|
command line.</para>
|
||||||
|
|
||||||
|
<para>Please note that setting the primary group to any arbitrary group
|
||||||
|
is no privileged operation on Windows. However, if this group is not
|
||||||
|
in your current user token, or if the group is in your user token but
|
||||||
|
marked as <literal>deny-only</literal>, no additional permissions can
|
||||||
|
be obtained by setting this group as primary group.</para>
|
||||||
|
</refsect1>
|
||||||
|
</refentry>
|
||||||
|
|
||||||
<refentry id="passwd">
|
<refentry id="passwd">
|
||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle>passwd</refentrytitle>
|
<refentrytitle>passwd</refentrytitle>
|
||||||
|
|
|
@ -87,6 +87,7 @@ pldd_LDADD = $(LDADD) -lpsapi
|
||||||
profiler_CXXFLAGS = -I$(srcdir) -idirafter ${top_srcdir}/cygwin/local_includes -idirafter ${top_srcdir}/cygwin/include $(AM_CXXFLAGS)
|
profiler_CXXFLAGS = -I$(srcdir) -idirafter ${top_srcdir}/cygwin/local_includes -idirafter ${top_srcdir}/cygwin/include $(AM_CXXFLAGS)
|
||||||
profiler_LDADD = $(LDADD) -lntdll
|
profiler_LDADD = $(LDADD) -lntdll
|
||||||
cygps_LDADD = $(LDADD) -lpsapi -lntdll
|
cygps_LDADD = $(LDADD) -lpsapi -lntdll
|
||||||
|
newgrp_LDADD = $(LDADD) -luserenv
|
||||||
|
|
||||||
if CROSS_BOOTSTRAP
|
if CROSS_BOOTSTRAP
|
||||||
SUBDIRS = mingw
|
SUBDIRS = mingw
|
||||||
|
|
|
@ -6,86 +6,204 @@ This software is a copyrighted work licensed under the terms of the
|
||||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||||
details. */
|
details. */
|
||||||
|
|
||||||
|
#define _GNU_SOURCE 1
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
|
||||||
|
#include <sys/cygwin.h>
|
||||||
|
#include <w32api/windows.h>
|
||||||
|
#include <w32api/userenv.h>
|
||||||
|
|
||||||
|
#define PATH_PREFIX "PATH=/usr/bin:"
|
||||||
|
|
||||||
|
char *
|
||||||
|
create_env_var (const char *name, const char *val)
|
||||||
|
{
|
||||||
|
char *var, *cp;
|
||||||
|
|
||||||
|
var = (char *) calloc (strlen (name) + strlen (val) + 2, sizeof (char *));
|
||||||
|
cp = stpcpy (var, name);
|
||||||
|
*cp++ = '=';
|
||||||
|
stpcpy (cp, val);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **
|
||||||
|
create_child_env (struct passwd *pw)
|
||||||
|
{
|
||||||
|
char **posix_env, *cp;
|
||||||
|
wchar_t *win_env, *wep;
|
||||||
|
size_t max_cnt = 0;
|
||||||
|
size_t idx = 0;
|
||||||
|
HANDLE token;
|
||||||
|
|
||||||
|
/* Fecth Windows default environment of current user */
|
||||||
|
if (!OpenProcessToken (GetCurrentProcess (),
|
||||||
|
TOKEN_QUERY | TOKEN_DUPLICATE, &token))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: creating environment failed with error %u "
|
||||||
|
"(OpenProcessToken)\n",
|
||||||
|
program_invocation_short_name, GetLastError ());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!CreateEnvironmentBlock ((PVOID *) &win_env, token, FALSE))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: creating environment failed with error %u "
|
||||||
|
"(CreateEnvironmentBlock)\n",
|
||||||
|
program_invocation_short_name, GetLastError ());
|
||||||
|
CloseHandle (token);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
CloseHandle (token);
|
||||||
|
/* Convert to Posix env */
|
||||||
|
for (wep = win_env; *wep; wep = wcschr (wep, '\0') + 1)
|
||||||
|
++max_cnt;
|
||||||
|
posix_env = (char **) calloc (max_cnt + 6, sizeof (char *));
|
||||||
|
if (!posix_env)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: allocating environment failed: %s\n",
|
||||||
|
program_invocation_short_name, strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (wep = win_env; *wep; ++idx, wep = wcschr (wep, '\0') + 1)
|
||||||
|
{
|
||||||
|
/* For $PATH we must prepend /usr/bin to the converted POSIX path list */
|
||||||
|
if (!wcsncasecmp (wep, L"PATH=", 5))
|
||||||
|
{
|
||||||
|
size_t len = cygwin_conv_path_list (CCP_WIN_W_TO_POSIX,
|
||||||
|
wep + 5, NULL, 0);
|
||||||
|
posix_env[idx] = (char *) calloc (sizeof (PATH_PREFIX) + len,
|
||||||
|
sizeof (char *));
|
||||||
|
if (!posix_env[idx])
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: allocating environment failed: %s\n",
|
||||||
|
program_invocation_short_name, strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cp = stpcpy (posix_env[idx], PATH_PREFIX);
|
||||||
|
cygwin_conv_path_list (CCP_WIN_W_TO_POSIX, wep + 5, cp, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t len = wcstombs (NULL, wep, 0);
|
||||||
|
|
||||||
|
if (len == (size_t) -1)
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"%s: invalid char in environment variable: %ls\n",
|
||||||
|
program_invocation_short_name, wep);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
posix_env[idx] = (char *) calloc (len + 1, sizeof (char *));
|
||||||
|
if (!posix_env[idx])
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: allocating environment failed: %s\n",
|
||||||
|
program_invocation_short_name, strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
wcstombs (posix_env[idx], wep, len + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DestroyEnvironmentBlock (win_env);
|
||||||
|
/* Add USER, LOGNAME, HOME, LANG, just like sshd */
|
||||||
|
posix_env[idx++] = create_env_var ("USER", pw->pw_name);
|
||||||
|
posix_env[idx++] = create_env_var ("LOGNAME", pw->pw_name);
|
||||||
|
posix_env[idx++] = create_env_var ("HOME", pw->pw_dir);
|
||||||
|
cp = getenv("LANG");
|
||||||
|
if (cp)
|
||||||
|
posix_env[idx] = create_env_var ("LANG", cp);
|
||||||
|
cp = getenv("TERM");
|
||||||
|
if (cp)
|
||||||
|
posix_env[idx] = create_env_var ("TERM", cp);
|
||||||
|
return posix_env;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, const char **argv)
|
main (int argc, const char **argv)
|
||||||
{
|
{
|
||||||
|
const char *cmd, **cmd_av, *fake_av[2];
|
||||||
|
struct passwd *pw;
|
||||||
struct group *gr;
|
struct group *gr;
|
||||||
|
char **child_env;
|
||||||
|
bool new_child_env = false;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
const char *cmd;
|
|
||||||
const char **cmd_av;
|
|
||||||
const char *fake_av[2];
|
|
||||||
|
|
||||||
/* TODO: Implement '-' option */
|
setlocale (LC_ALL, "");
|
||||||
/* TODO: Add command description to documentation */
|
|
||||||
|
|
||||||
if (argc < 2 || argv[1][0] == '-')
|
if (argc < 2 || (argv[1][0] == '-' && argv[1][1]))
|
||||||
{
|
{
|
||||||
fprintf (stderr,
|
fprintf (stderr, "Usage: %s [-] [group] [command [args...]]\n",
|
||||||
"Usage: %1$s group [command [args...]]\n"
|
|
||||||
"\n"
|
|
||||||
"%1$s changes the current primary group for a command.\n"
|
|
||||||
"The primary group must be member of the supplementary group\n"
|
|
||||||
"list of the user.\n"
|
|
||||||
"The command and its arguments are specified on the command\n"
|
|
||||||
"line. Default is the user's standard shell.\n",
|
|
||||||
program_invocation_short_name);
|
program_invocation_short_name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (isdigit ((int) argv[1][0]))
|
|
||||||
{
|
|
||||||
char *e = NULL;
|
|
||||||
|
|
||||||
gid = strtol (argv[1], &e, 10);
|
/* Check if we have to regenerate a stock environment */
|
||||||
if (e && *e != '\0')
|
if (argv[1][0] == '-')
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s: invalid gid `%s'\n",
|
new_child_env = true;
|
||||||
program_invocation_short_name, argv[1]);
|
--argc;
|
||||||
return 2;
|
++argv;
|
||||||
}
|
}
|
||||||
gr = getgrgid (gid);
|
|
||||||
if (!gr)
|
pw = getpwuid (getuid ());
|
||||||
|
|
||||||
|
/* Fetch group */
|
||||||
|
if (argv[1] == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s: unknown group gid `%u'\n",
|
gid = pw->pw_gid;
|
||||||
program_invocation_short_name, gid);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gr = getgrnam (argv[1]);
|
gr = getgrnam (argv[1]);
|
||||||
if (!gr)
|
if (!gr)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s: unknown group name `%s'\n",
|
fprintf (stderr, "%s: group '%s' does not exist\n",
|
||||||
program_invocation_short_name, argv[1]);
|
program_invocation_short_name, argv[1]);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
gid = gr->gr_gid;
|
gid = gr->gr_gid;
|
||||||
|
--argc;
|
||||||
|
++argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set primary group */
|
||||||
if (setgid (gid) != 0)
|
if (setgid (gid) != 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s: can't switch primary group to `%s'\n",
|
fprintf (stderr, "%s: can't switch primary group to '%s'\n",
|
||||||
program_invocation_short_name, argv[1]);
|
program_invocation_short_name, argv[1]);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
argc -= 2;
|
|
||||||
argv += 2;
|
/* Maybe generate stock child environment */
|
||||||
|
if (!new_child_env)
|
||||||
|
child_env = environ;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child_env = create_child_env (pw);
|
||||||
|
if (!child_env)
|
||||||
|
return 3;
|
||||||
|
chdir (pw->pw_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set argc/argv for execvpe */
|
||||||
|
--argc;
|
||||||
|
++argv;
|
||||||
if (argc < 1)
|
if (argc < 1)
|
||||||
{
|
{
|
||||||
struct passwd *pw = getpwuid (getuid ());
|
|
||||||
if (!pw)
|
if (!pw)
|
||||||
cmd = "/usr/bin/bash";
|
cmd = "/usr/bin/bash";
|
||||||
else
|
else
|
||||||
cmd = pw->pw_shell;
|
cmd = pw->pw_shell;
|
||||||
fake_av[0] = cmd;
|
fake_av[0] = new_child_env ? "-" : cmd;
|
||||||
fake_av[1] = NULL;
|
fake_av[1] = NULL;
|
||||||
cmd_av = fake_av;
|
cmd_av = fake_av;
|
||||||
}
|
}
|
||||||
|
@ -94,8 +212,12 @@ main (int argc, const char **argv)
|
||||||
cmd = argv[0];
|
cmd = argv[0];
|
||||||
cmd_av = argv;
|
cmd_av = argv;
|
||||||
}
|
}
|
||||||
execvp (cmd, (char **) cmd_av);
|
|
||||||
fprintf (stderr, "%s: failed to start `%s': %s\n",
|
/* Exec child process */
|
||||||
|
execvpe (cmd, (char **) cmd_av, child_env);
|
||||||
|
|
||||||
|
/* Oops */
|
||||||
|
fprintf (stderr, "%s: failed to start '%s': %s\n",
|
||||||
program_invocation_short_name, cmd, strerror (errno));
|
program_invocation_short_name, cmd, strerror (errno));
|
||||||
return 3;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue