1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31
32#include <locale.h>
33#include <stdio.h>
34#include <pwd.h>
35#include <grp.h>
36#include <sys/param.h>
37#include <unistd.h>
38#include <string.h>
39#include <project.h>
40#include <stdlib.h>
41#include <alloca.h>
42
43#define	PWNULL  ((struct passwd *)0)
44#define	GRNULL  ((struct group *)0)
45
46typedef enum TYPE {
47	UID, EUID, GID, EGID, SGID
48}	TYPE;
49
50typedef enum PRINT {
51	CURR,		/* Print uid/gid only */
52	ALLGROUPS,	/* Print all groups */
53	GROUP,		/* Print only group */
54	USER		/* Print only uid */
55}	PRINT;
56static PRINT mode = CURR;
57
58static int usage(void);
59static void puid(uid_t);
60static void pgid(gid_t);
61static void prid(TYPE, uid_t);
62static int getusergroups(int, gid_t *, char *, gid_t);
63
64static int nflag = 0;		/* Output names, not numbers */
65static int rflag = 0;		/* Output real, not effective IDs */
66static char stdbuf[BUFSIZ];
67
68int
69main(int argc, char *argv[])
70{
71	gid_t *idp;
72	uid_t uid, euid;
73	gid_t gid, egid, prgid;
74	int c, aflag = 0, project_flag = 0;
75	struct passwd *pwp;
76	int i, j;
77	int groupmax = sysconf(_SC_NGROUPS_MAX);
78	gid_t *groupids = alloca(groupmax * sizeof (gid_t));
79	struct group *gr;
80	char *user = NULL;
81
82	(void) setlocale(LC_ALL, "");
83
84#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
85#define	TEXT_DOMAIN "SYS_TEST"
86#endif
87	(void) textdomain(TEXT_DOMAIN);
88	while ((c = getopt(argc, argv, "Ggunarp")) != EOF) {
89		switch (c) {
90			case 'G':
91				if (mode != CURR)
92					return (usage());
93				mode = ALLGROUPS;
94				break;
95
96			case 'g':
97				if (mode != CURR)
98					return (usage());
99				mode = GROUP;
100				break;
101
102			case 'a':
103				aflag++;
104				break;
105
106			case 'n':
107				nflag++;
108				break;
109
110			case 'r':
111				rflag++;
112				break;
113
114			case 'u':
115				if (mode != CURR)
116					return (usage());
117				mode = USER;
118				break;
119
120			case 'p':
121				if (mode != CURR)
122					return (usage());
123				project_flag++;
124				break;
125
126			case '?':
127				return (usage());
128		}
129	}
130	setbuf(stdout, stdbuf);
131	argc -= optind-1;
132	argv += optind-1;
133
134	/* -n and -r must be combined with one of -[Ggu] */
135	/* -r cannot be combined with -G */
136	/* -a and -p cannot be combined with -[Ggu] */
137
138	if ((mode == CURR && (nflag || rflag)) ||
139		(mode == ALLGROUPS && rflag) ||
140		(argc != 1 && argc != 2) ||
141		(mode != CURR && (project_flag || aflag)))
142		return (usage());
143	if (argc == 2) {
144		if ((pwp = getpwnam(argv[1])) == PWNULL) {
145			(void) fprintf(stderr,
146				gettext("id: invalid user name: \"%s\"\n"),
147					argv[1]);
148			return (1);
149		}
150		user = argv[1];
151		uid = euid = pwp->pw_uid;
152		prgid = gid = egid = pwp->pw_gid;
153	} else {
154		uid = getuid();
155		gid = getgid();
156		euid = geteuid();
157		egid = getegid();
158	}
159
160	if (mode != CURR) {
161		if (!rflag) {
162			uid = euid;
163			gid = egid;
164		}
165		if (mode == USER)
166			puid(uid);
167		else if (mode == GROUP)
168			pgid(gid);
169		else if (mode == ALLGROUPS) {
170			pgid(gid);
171			if (user)
172				i = getusergroups(groupmax, groupids, user,
173				    prgid);
174			else
175				i = getgroups(groupmax, groupids);
176			if (i == -1)
177				perror("getgroups");
178			else if (i > 0) {
179				for (j = 0; j < i; ++j) {
180					if ((gid = groupids[j]) == egid)
181						continue;
182					(void) putchar(' ');
183					pgid(gid);
184				}
185			}
186		}
187		(void) putchar('\n');
188	} else {
189		prid(UID, uid);
190		prid(GID, gid);
191		if (uid != euid)
192			prid(EUID, euid);
193		if (gid != egid)
194			prid(EGID, egid);
195
196		if (aflag) {
197			if (user)
198				i = getusergroups(groupmax, groupids, user,
199				    prgid);
200			else
201				i = getgroups(groupmax, groupids);
202			if (i == -1)
203				perror("getgroups");
204			else if (i > 0) {
205				(void) printf(" groups=");
206				for (idp = groupids; i--; idp++) {
207					(void) printf("%u", *idp);
208					if (gr = getgrgid(*idp))
209						(void) printf("(%s)",
210							gr->gr_name);
211					if (i)
212						(void) putchar(',');
213				}
214			}
215		}
216#ifdef XPG4
217		/*
218		 * POSIX requires us to show all supplementary groups
219		 * groups other than the effective group already listed.
220		 *
221		 * This differs from -a above, because -a always shows
222		 * all groups including the effective group in the group=
223		 * line.
224		 *
225		 * It would be simpler if SunOS could just adopt this
226		 * POSIX behavior, as it is so incredibly close to the
227		 * the norm already.
228		 *
229		 * Then the magic -a flag could just indicate whether or
230		 * not we are suppressing the effective group id.
231		 */
232		else {
233			if (user)
234				i = getusergroups(groupmax, groupids, user,
235				    prgid);
236			else
237				i = getgroups(groupmax, groupids);
238			if (i == -1)
239				perror("getgroups");
240			else if (i > 1) {
241				(void) printf(" groups=");
242				for (idp = groupids; i--; idp++) {
243					if (*idp == egid)
244						continue;
245					(void) printf("%u", *idp);
246					if (gr = getgrgid(*idp))
247						(void) printf("(%s)",
248							gr->gr_name);
249					if (i)
250						(void) putchar(',');
251				}
252			}
253		}
254#endif
255		if (project_flag) {
256			struct project proj;
257			void *projbuf;
258			projid_t curprojid = getprojid();
259
260			if ((projbuf = malloc(PROJECT_BUFSZ)) == NULL) {
261				(void) fprintf(stderr, "unable to allocate "
262				    "memory\n");
263				return (2);
264			}
265
266			if (user) {
267				if (getdefaultproj(user, &proj, projbuf,
268				    PROJECT_BUFSZ) != NULL)
269					(void) printf(" projid=%d(%s)",
270					    (int)proj.pj_projid, proj.pj_name);
271				else
272					/*
273					 * This can only happen if project
274					 * "default" has been removed from
275					 * /etc/project file or the whole
276					 * project database file was removed.
277					 */
278					(void) printf(" projid=(NONE)");
279			} else {
280				if (getprojbyid(curprojid, &proj, projbuf,
281				    PROJECT_BUFSZ) == NULL)
282					(void) printf(" projid=%d",
283					    (int)curprojid);
284				else
285					(void) printf(" projid=%d(%s)",
286					    (int)curprojid, proj.pj_name);
287			}
288			free(projbuf);
289		}
290		(void) putchar('\n');
291	}
292	return (0);
293}
294
295static int
296usage()
297{
298	(void) fprintf(stderr, gettext(
299	    "Usage: id [-ap] [user]\n"
300	    "       id -G [-n] [user]\n"
301	    "       id -g [-nr] [user]\n"
302	    "       id -u [-nr] [user]\n"));
303	return (2);
304}
305
306static void
307puid(uid_t uid)
308{
309	struct passwd *pw;
310
311	if (nflag && (pw = getpwuid(uid)) != PWNULL)
312		(void) printf("%s", pw->pw_name);
313	else
314		(void) printf("%u", uid);
315}
316
317static void
318pgid(gid_t gid)
319{
320	struct group *gr;
321
322	if (nflag && (gr = getgrgid(gid)) != GRNULL)
323		(void) printf("%s", gr->gr_name);
324	else
325		(void) printf("%u", gid);
326}
327
328static void
329prid(TYPE how, uid_t id)
330{
331	char *s;
332
333	switch ((int)how) {
334		case UID:
335			s = "uid";
336			break;
337
338		case EUID:
339			s = " euid";
340			break;
341
342		case GID:
343			s = " gid";
344			break;
345
346		case EGID:
347			s = " egid";
348			break;
349
350	}
351	if (s != NULL)
352		(void) printf("%s=", s);
353	(void) printf("%u", id);
354	switch ((int)how) {
355	case UID:
356	case EUID:
357		{
358			struct passwd *pwp;
359
360			if ((pwp = getpwuid(id)) != PWNULL)
361				(void) printf("(%s)", pwp->pw_name);
362
363		}
364		break;
365	case GID:
366	case EGID:
367		{
368			struct group *grp;
369
370			if ((grp = getgrgid(id)) != GRNULL)
371				(void) printf("(%s)", grp->gr_name);
372		}
373		break;
374	}
375}
376
377/*
378 * Get the supplementary group affiliation for the user
379 */
380static int getusergroups(gidsetsize, grouplist, user, prgid)
381int	gidsetsize;
382gid_t	*grouplist;
383char	*user;
384gid_t	prgid;
385{
386	struct group *group;
387	char **gr_mem;
388	int ngroups = 0;
389
390	setgrent();
391	while ((ngroups < gidsetsize) && ((group = getgrent()) != NULL))
392		for (gr_mem = group->gr_mem; *gr_mem; gr_mem++)
393			if (strcmp(user, *gr_mem) == 0) {
394				if (gidsetsize)
395					grouplist[ngroups] = group->gr_gid;
396				ngroups++;
397			}
398	endgrent();
399	if (gidsetsize && !ngroups)
400		grouplist[ngroups++] = prgid;
401	return (ngroups);
402}
403