ls: fix comment about -p (it's compatible now)
[platform/upstream/busybox.git] / coreutils / id.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini id implementation for busybox
4  *
5  * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
6  * Copyright (C) 2008 by Tito Ragusa <farmatito@tiscali.it>
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9  */
10
11 /* BB_AUDIT SUSv3 compliant. */
12 /* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever
13  * length and to be more similar to GNU id.
14  * -Z option support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
15  * Added -G option Tito Ragusa (C) 2008 for SUSv3.
16  */
17
18 //usage:#define id_trivial_usage
19 //usage:       "[OPTIONS] [USER]"
20 //usage:#define id_full_usage "\n\n"
21 //usage:       "Print information about USER or the current user\n"
22 //usage:        IF_SELINUX(
23 //usage:     "\n        -Z      Security context"
24 //usage:        )
25 //usage:     "\n        -u      User ID"
26 //usage:     "\n        -g      Group ID"
27 //usage:     "\n        -G      Supplementary group IDs"
28 //usage:     "\n        -n      Print names instead of numbers"
29 //usage:     "\n        -r      Print real ID instead of effective ID"
30 //usage:
31 //usage:#define id_example_usage
32 //usage:       "$ id\n"
33 //usage:       "uid=1000(andersen) gid=1000(andersen)\n"
34
35 #include "libbb.h"
36
37 /* This is a NOEXEC applet. Be very careful! */
38
39 #if !ENABLE_USE_BB_PWD_GRP
40 #if defined(__UCLIBC_MAJOR__) && (__UCLIBC_MAJOR__ == 0)
41 #if (__UCLIBC_MINOR__ < 9) || (__UCLIBC_MINOR__ == 9 &&  __UCLIBC_SUBLEVEL__ < 30)
42 #error "Sorry, you need at least uClibc version 0.9.30 for id applet to build"
43 #endif
44 #endif
45 #endif
46
47 enum {
48         PRINT_REAL      = (1 << 0),
49         NAME_NOT_NUMBER = (1 << 1),
50         JUST_USER       = (1 << 2),
51         JUST_GROUP      = (1 << 3),
52         JUST_ALL_GROUPS = (1 << 4),
53 #if ENABLE_SELINUX
54         JUST_CONTEXT    = (1 << 5),
55 #endif
56 };
57
58 static int print_common(unsigned id, const char *name, const char *prefix)
59 {
60         if (prefix) {
61                 printf("%s", prefix);
62         }
63         if (!(option_mask32 & NAME_NOT_NUMBER) || !name) {
64                 printf("%u", id);
65         }
66         if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) {
67                 if (name) {
68                         printf(option_mask32 ? "%s" : "(%s)", name);
69                 } else {
70                         /* Don't set error status flag in default mode */
71                         if (option_mask32) {
72                                 if (ENABLE_DESKTOP)
73                                         bb_error_msg("unknown ID %u", id);
74                                 return EXIT_FAILURE;
75                         }
76                 }
77         }
78         return EXIT_SUCCESS;
79 }
80
81 static int print_group(gid_t id, const char *prefix)
82 {
83         return print_common(id, gid2group(id), prefix);
84 }
85
86 static int print_user(uid_t id, const char *prefix)
87 {
88         return print_common(id, uid2uname(id), prefix);
89 }
90
91 /* On error set *n < 0 and return >= 0
92  * If *n is too small, update it and return < 0
93  *  (ok to trash groups[] in both cases)
94  * Otherwise fill in groups[] and return >= 0
95  */
96 static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
97 {
98         int m;
99
100         if (username) {
101                 /* If the user is a member of more than
102                  * *n groups, then -1 is returned. Otherwise >= 0.
103                  * (and no defined way of detecting errors?!) */
104                 m = getgrouplist(username, rgid, groups, n);
105                 /* I guess *n < 0 might indicate error. Anyway,
106                  * malloc'ing -1 bytes won't be good, so: */
107                 //if (*n < 0)
108                 //      return 0;
109                 //return m;
110                 //commented out here, happens below anyway
111         } else {
112                 /* On error -1 is returned, which ends up in *n */
113                 int nn = getgroups(*n, groups);
114                 /* 0: nn <= *n, groups[] was big enough; -1 otherwise */
115                 m = - (nn > *n);
116                 *n = nn;
117         }
118         if (*n < 0)
119                 return 0; /* error, don't return < 0! */
120         return m;
121 }
122
123 int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
124 int id_main(int argc UNUSED_PARAM, char **argv)
125 {
126         uid_t ruid;
127         gid_t rgid;
128         uid_t euid;
129         gid_t egid;
130         unsigned opt;
131         int i;
132         int status = EXIT_SUCCESS;
133         const char *prefix;
134         const char *username;
135 #if ENABLE_SELINUX
136         security_context_t scontext = NULL;
137 #endif
138         /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
139         /* Don't allow more than one username */
140         opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
141                          IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
142         opt = getopt32(argv, "rnugG" IF_SELINUX("Z"));
143
144         username = argv[optind];
145         if (username) {
146                 struct passwd *p = xgetpwnam(username);
147                 euid = ruid = p->pw_uid;
148                 egid = rgid = p->pw_gid;
149         } else {
150                 egid = getegid();
151                 rgid = getgid();
152                 euid = geteuid();
153                 ruid = getuid();
154         }
155         /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */
156         /* id says: print the real ID instead of the effective ID, with -ugG */
157         /* in fact in this case egid is always printed if egid != rgid */
158         if (!opt || (opt & JUST_ALL_GROUPS)) {
159                 gid_t *groups;
160                 int n;
161
162                 if (!opt) {
163                         /* Default Mode */
164                         status |= print_user(ruid, "uid=");
165                         status |= print_group(rgid, " gid=");
166                         if (euid != ruid)
167                                 status |= print_user(euid, " euid=");
168                         if (egid != rgid)
169                                 status |= print_group(egid, " egid=");
170                 } else {
171                         /* JUST_ALL_GROUPS */
172                         status |= print_group(rgid, NULL);
173                         if (egid != rgid)
174                                 status |= print_group(egid, " ");
175                 }
176                 /* We are supplying largish buffer, trying
177                  * to not run get_groups() twice. That might be slow
178                  * ("user database in remote SQL server" case) */
179                 groups = xmalloc(64 * sizeof(gid_t));
180                 n = 64;
181                 if (get_groups(username, rgid, groups, &n) < 0) {
182                         /* Need bigger buffer after all */
183                         groups = xrealloc(groups, n * sizeof(gid_t));
184                         get_groups(username, rgid, groups, &n);
185                 }
186                 if (n > 0) {
187                         /* Print the list */
188                         prefix = " groups=";
189                         for (i = 0; i < n; i++) {
190                                 if (opt && (groups[i] == rgid || groups[i] == egid))
191                                         continue;
192                                 status |= print_group(groups[i], opt ? " " : prefix);
193                                 prefix = ",";
194                         }
195                 } else if (n < 0) { /* error in get_groups() */
196                         if (ENABLE_DESKTOP)
197                                 bb_error_msg_and_die("can't get groups");
198                         return EXIT_FAILURE;
199                 }
200                 if (ENABLE_FEATURE_CLEAN_UP)
201                         free(groups);
202 #if ENABLE_SELINUX
203                 if (is_selinux_enabled()) {
204                         if (getcon(&scontext) == 0)
205                                 printf(" context=%s", scontext);
206                 }
207 #endif
208         } else if (opt & PRINT_REAL) {
209                 euid = ruid;
210                 egid = rgid;
211         }
212
213         if (opt & JUST_USER)
214                 status |= print_user(euid, NULL);
215         else if (opt & JUST_GROUP)
216                 status |= print_group(egid, NULL);
217 #if ENABLE_SELINUX
218         else if (opt & JUST_CONTEXT) {
219                 selinux_or_die();
220                 if (username || getcon(&scontext)) {
221                         bb_error_msg_and_die("can't get process context%s",
222                                 username ? " for a different user" : "");
223                 }
224                 fputs(scontext, stdout);
225         }
226         /* freecon(NULL) seems to be harmless */
227         if (ENABLE_FEATURE_CLEAN_UP)
228                 freecon(scontext);
229 #endif
230         bb_putchar('\n');
231         fflush_stdout_and_exit(status);
232 }