xref: /illumos-gate/usr/src/cmd/id/id.c (revision f48205be)
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 
46 typedef enum TYPE {
47 	UID, EUID, GID, EGID, SGID
48 }	TYPE;
49 
50 typedef 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;
56 static PRINT mode = CURR;
57 
58 static int usage(void);
59 static void puid(uid_t);
60 static void pgid(gid_t);
61 static void prid(TYPE, uid_t);
62 static int getusergroups(int, gid_t *, char *, gid_t);
63 
64 static int nflag = 0;		/* Output names, not numbers */
65 static int rflag = 0;		/* Output real, not effective IDs */
66 static char stdbuf[BUFSIZ];
67 
68 int
69 main(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 
295 static int
296 usage()
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 
306 static void
307 puid(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 
317 static void
318 pgid(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 
328 static void
329 prid(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  */
380 static int getusergroups(gidsetsize, grouplist, user, prgid)
381 int	gidsetsize;
382 gid_t	*grouplist;
383 char	*user;
384 gid_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