1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2003 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
27 #include <sys/types.h>
32 # include <sys/param.h>
59 #define _(msgid) gettext (msgid)
60 #define N_(msgid) msgid
62 #ifndef _POSIX_VERSION
63 struct passwd *getpwnam ();
64 struct group *getgrnam ();
65 struct group *getgrgid ();
69 # define endgrent() ((void) 0)
73 # define endpwent() ((void) 0)
76 /* The extra casts work around common compiler bugs. */
77 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
78 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
79 It is necessary at least when t == time_t. */
80 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
81 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
82 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
85 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
89 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
92 /* MAXUID may come from limits.h or sys/params.h. */
94 # define MAXUID UID_T_MAX
97 # define MAXGID GID_T_MAX
100 /* Perform the equivalent of the statement `dest = strdup (src);',
101 but obtaining storage via alloca instead of from the heap. */
103 #define V_STRDUP(dest, src) \
106 int _len = strlen ((src)); \
107 (dest) = (char *) alloca (_len + 1); \
108 strcpy (dest, src); \
112 /* ISDIGIT differs from isdigit, as follows:
113 - Its arg may be any int or unsigned int; it need not be an unsigned char.
114 - It's guaranteed to evaluate its argument exactly once.
115 - It's typically faster.
116 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
117 ISDIGIT_LOCALE unless it's important to use the locale's definition
118 of `digit' even when the host does not conform to POSIX. */
119 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
125 /* Return nonzero if STR represents an unsigned decimal integer,
126 otherwise return 0. */
129 is_number (const char *str)
137 /* Extract from NAME, which has the form "[user][:.][group]",
138 a USERNAME, UID U, GROUPNAME, and GID G.
139 Either user or group, or both, must be present.
140 If the group is omitted but the ":" separator is given,
141 use the given user's login group.
142 If SPEC_ARG contains a `:', then use that as the separator, ignoring
143 any `.'s. If there is no `:', but there is a `.', then first look
144 up the entire SPEC_ARG as a login name. If that look-up fails, then
145 try again interpreting the `.' as a separator.
147 USERNAME and GROUPNAME will be in newly malloc'd memory.
148 Either one might be NULL instead, indicating that it was not
149 given and the corresponding numeric ID was left unchanged.
151 Return NULL if successful, a static error message string if not. */
154 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
155 char **username_arg, char **groupname_arg)
157 static const char *E_invalid_user = N_("invalid user");
158 static const char *E_invalid_group = N_("invalid group");
159 static const char *E_bad_spec =
160 N_("cannot get the login group of a numeric UID");
161 static const char *E_cannot_omit_both =
162 N_("cannot omit both user and group");
164 const char *error_msg;
165 char *spec; /* A copy we can write on. */
168 char *g, *u, *separator;
174 *username_arg = *groupname_arg = NULL;
177 V_STRDUP (spec, spec_arg);
179 /* Find the POSIX `:' separator if there is one. */
180 separator = strchr (spec, ':');
182 /* If there is no colon, then see if there's a `.'. */
183 if (separator == NULL && posix2_version () < 200112)
185 dot = strchr (spec, '.');
186 /* If there's no colon but there is a `.', then first look up the
187 whole spec, in case it's an OWNER name that includes a dot.
188 If that fails, then we'll try again, but interpreting the `.'
190 /* FIXME: accepting `.' as the separator is contrary to POSIX.
191 someday we should drop support for this. */
198 /* Replace separator with a NUL. */
199 if (separator != NULL)
202 /* Set U and G to non-zero length strings corresponding to user and
203 group specifiers or to NULL. */
204 u = (*spec == '\0' ? NULL : spec);
206 g = (separator == NULL || *(separator + 1) == '\0'
210 if (u == NULL && g == NULL)
211 return _(E_cannot_omit_both);
214 /* Pretend that we are the user U whose group is G. This makes
215 pwd and grp functions ``know'' about the UID and GID of these. */
216 if (u && !is_number (u))
217 setenv ("USER", u, 1);
218 if (g && !is_number (g))
219 setenv ("GROUP", g, 1);
229 error_msg = E_invalid_user;
233 use_login_group = (separator != NULL && g == NULL);
235 error_msg = E_bad_spec;
238 unsigned long int tmp_long;
239 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
240 || tmp_long > MAXUID)
241 return _(E_invalid_user);
249 if (g == NULL && separator != NULL)
251 /* A separator was given, but a group was not specified,
252 so get the login group. */
254 grp = getgrgid (pwd->pw_gid);
257 /* This is enough room to hold the unsigned decimal
258 representation of any 32-bit quantity and the trailing
261 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
262 V_STRDUP (groupname, uint_buf);
266 V_STRDUP (groupname, grp->gr_name);
274 if (g != NULL && error_msg == NULL)
276 /* Explicit group. */
281 error_msg = E_invalid_group;
284 unsigned long int tmp_long;
285 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
286 || tmp_long > MAXGID)
287 return _(E_invalid_group);
293 endgrent (); /* Save a file descriptor. */
295 if (error_msg == NULL)
296 V_STRDUP (groupname, g);
299 if (error_msg == NULL)
303 *username_arg = strdup (u);
304 if (*username_arg == NULL)
305 error_msg = xalloc_msg_memory_exhausted;
308 if (groupname != NULL && error_msg == NULL)
310 *groupname_arg = strdup (groupname);
311 if (*groupname_arg == NULL)
313 if (*username_arg != NULL)
315 free (*username_arg);
316 *username_arg = NULL;
318 error_msg = xalloc_msg_memory_exhausted;
323 if (error_msg && maybe_retry)
336 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
339 main (int argc, char **argv)
343 for (i = 1; i < argc; i++)
346 char *username, *groupname;
351 tmp = strdup (argv[i]);
352 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
354 printf ("%s: %u %u %s %s %s\n",
358 NULL_CHECK (username),
359 NULL_CHECK (groupname),