1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2003 Free Software
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
28 #include <sys/types.h>
33 # include <sys/param.h>
50 #define _(msgid) gettext (msgid)
51 #define N_(msgid) msgid
53 #ifndef _POSIX_VERSION
54 struct passwd *getpwnam ();
55 struct group *getgrnam ();
56 struct group *getgrgid ();
60 # define endgrent() ((void) 0)
64 # define endpwent() ((void) 0)
67 /* The extra casts work around common compiler bugs. */
68 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
69 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
70 It is necessary at least when t == time_t. */
71 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
72 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
73 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
76 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
80 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
83 /* MAXUID may come from limits.h or sys/params.h. */
85 # define MAXUID UID_T_MAX
88 # define MAXGID GID_T_MAX
91 /* Perform the equivalent of the statement `dest = strdup (src);',
92 but obtaining storage via alloca instead of from the heap. */
94 #define V_STRDUP(dest, src) \
97 int _len = strlen ((src)); \
98 (dest) = (char *) alloca (_len + 1); \
103 /* ISDIGIT differs from isdigit, as follows:
104 - Its arg may be any int or unsigned int; it need not be an unsigned char.
105 - It's guaranteed to evaluate its argument exactly once.
106 - It's typically faster.
107 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
108 ISDIGIT_LOCALE unless it's important to use the locale's definition
109 of `digit' even when the host does not conform to POSIX. */
110 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
116 /* Return nonzero if STR represents an unsigned decimal integer,
117 otherwise return 0. */
120 is_number (const char *str)
128 /* Extract from NAME, which has the form "[user][:.][group]",
129 a USERNAME, UID U, GROUPNAME, and GID G.
130 Either user or group, or both, must be present.
131 If the group is omitted but the ":" separator is given,
132 use the given user's login group.
133 If SPEC_ARG contains a `:', then use that as the separator, ignoring
134 any `.'s. If there is no `:', but there is a `.', then first look
135 up the entire SPEC_ARG as a login name. If that look-up fails, then
136 try again interpreting the `.' as a separator.
138 USERNAME and GROUPNAME will be in newly malloc'd memory.
139 Either one might be NULL instead, indicating that it was not
140 given and the corresponding numeric ID was left unchanged.
142 Return NULL if successful, a static error message string if not. */
145 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
146 char **username_arg, char **groupname_arg)
148 static const char *E_invalid_user = N_("invalid user");
149 static const char *E_invalid_group = N_("invalid group");
150 static const char *E_bad_spec =
151 N_("cannot get the login group of a numeric UID");
152 static const char *E_cannot_omit_both =
153 N_("cannot omit both user and group");
155 const char *error_msg;
156 char *spec; /* A copy we can write on. */
159 char *g, *u, *separator;
165 *username_arg = *groupname_arg = NULL;
168 V_STRDUP (spec, spec_arg);
170 /* Find the POSIX `:' separator if there is one. */
171 separator = strchr (spec, ':');
173 /* If there is no colon, then see if there's a `.'. */
174 if (separator == NULL && posix2_version () < 200112)
176 dot = strchr (spec, '.');
177 /* If there's no colon but there is a `.', then first look up the
178 whole spec, in case it's an OWNER name that includes a dot.
179 If that fails, then we'll try again, but interpreting the `.'
181 /* FIXME: accepting `.' as the separator is contrary to POSIX.
182 someday we should drop support for this. */
189 /* Replace separator with a NUL. */
190 if (separator != NULL)
193 /* Set U and G to non-zero length strings corresponding to user and
194 group specifiers or to NULL. */
195 u = (*spec == '\0' ? NULL : spec);
197 g = (separator == NULL || *(separator + 1) == '\0'
201 if (u == NULL && g == NULL)
202 return _(E_cannot_omit_both);
205 /* Pretend that we are the user U whose group is G. This makes
206 pwd and grp functions ``know'' about the UID and GID of these. */
207 if (u && !is_number (u))
208 setenv ("USER", u, 1);
209 if (g && !is_number (g))
210 setenv ("GROUP", g, 1);
220 error_msg = E_invalid_user;
224 use_login_group = (separator != NULL && g == NULL);
226 error_msg = E_bad_spec;
229 unsigned long int tmp_long;
230 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
231 || tmp_long > MAXUID)
232 return _(E_invalid_user);
240 if (g == NULL && separator != NULL)
242 /* A separator was given, but a group was not specified,
243 so get the login group. */
245 grp = getgrgid (pwd->pw_gid);
248 /* This is enough room to hold the unsigned decimal
249 representation of any 32-bit quantity and the trailing
252 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
253 V_STRDUP (groupname, uint_buf);
257 V_STRDUP (groupname, grp->gr_name);
265 if (g != NULL && error_msg == NULL)
267 /* Explicit group. */
272 error_msg = E_invalid_group;
275 unsigned long int tmp_long;
276 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
277 || tmp_long > MAXGID)
278 return _(E_invalid_group);
284 endgrent (); /* Save a file descriptor. */
286 if (error_msg == NULL)
287 V_STRDUP (groupname, g);
290 if (error_msg == NULL)
294 *username_arg = strdup (u);
295 if (*username_arg == NULL)
296 error_msg = xalloc_msg_memory_exhausted;
299 if (groupname != NULL && error_msg == NULL)
301 *groupname_arg = strdup (groupname);
302 if (*groupname_arg == NULL)
304 if (*username_arg != NULL)
306 free (*username_arg);
307 *username_arg = NULL;
309 error_msg = xalloc_msg_memory_exhausted;
314 if (error_msg && maybe_retry)
327 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
330 main (int argc, char **argv)
334 for (i = 1; i < argc; i++)
337 char *username, *groupname;
342 tmp = strdup (argv[i]);
343 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
345 printf ("%s: %u %u %s %s %s\n",
349 NULL_CHECK (username),
350 NULL_CHECK (groupname),