TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / id.c
1 /* id -- print real and effective UIDs and GIDs
2    Copyright (C) 1989-2008 Free Software Foundation, Inc.
3
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 3 of the License, or
7    (at your option) any later version.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Arnold Robbins.
18    Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu. */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <getopt.h>
26 #include <selinux/selinux.h>
27
28 #include "system.h"
29 #include "error.h"
30 #include "mgetgroups.h"
31 #include "quote.h"
32 #include "group-list.h"
33
34 /* The official name of this program (e.g., no `g' prefix).  */
35 #define PROGRAM_NAME "id"
36
37 #define AUTHORS \
38   proper_name ("Arnold Robbins"), \
39   proper_name ("David MacKenzie")
40
41 /* If nonzero, output only the SELinux context. -Z */
42 static int just_context = 0;
43
44 static void print_user (uid_t uid);
45 static void print_full_info (const char *username);
46
47 /* If true, output user/group name instead of ID number. -n */
48 static bool use_name = false;
49
50 /* The real and effective IDs of the user to print. */
51 static uid_t ruid, euid;
52 static gid_t rgid, egid;
53
54 /* True unless errors have been encountered.  */
55 static bool ok = true;
56
57 /* The SELinux context.  Start with a known invalid value so print_full_info
58    knows when `context' has not been set to a meaningful value.  */
59 static security_context_t context = NULL;
60
61 static struct option const longopts[] =
62 {
63   {"context", no_argument, NULL, 'Z'},
64   {"group", no_argument, NULL, 'g'},
65   {"groups", no_argument, NULL, 'G'},
66   {"name", no_argument, NULL, 'n'},
67   {"real", no_argument, NULL, 'r'},
68   {"user", no_argument, NULL, 'u'},
69   {GETOPT_HELP_OPTION_DECL},
70   {GETOPT_VERSION_OPTION_DECL},
71   {NULL, 0, NULL, 0}
72 };
73
74 void
75 usage (int status)
76 {
77   if (status != EXIT_SUCCESS)
78     fprintf (stderr, _("Try `%s --help' for more information.\n"),
79              program_name);
80   else
81     {
82       printf (_("Usage: %s [OPTION]... [USERNAME]\n"), program_name);
83       fputs (_("\
84 Print information for USERNAME, or the current user.\n\
85 \n\
86   -a              ignore, for compatibility with other versions\n\
87   -Z, --context   print only the security context of the current user\n\
88   -g, --group     print only the effective group ID\n\
89   -G, --groups    print all group IDs\n\
90   -n, --name      print a name instead of a number, for -ugG\n\
91   -r, --real      print the real ID instead of the effective ID, with -ugG\n\
92   -u, --user      print only the effective user ID\n\
93 "), stdout);
94       fputs (HELP_OPTION_DESCRIPTION, stdout);
95       fputs (VERSION_OPTION_DESCRIPTION, stdout);
96       fputs (_("\
97 \n\
98 Without any OPTION, print some useful set of identified information.\n\
99 "), stdout);
100       emit_bug_reporting_address ();
101     }
102   exit (status);
103 }
104
105 int
106 main (int argc, char **argv)
107 {
108   int optc;
109   int selinux_enabled = (is_selinux_enabled () > 0);
110
111   /* If true, output the list of all group IDs. -G */
112   bool just_group_list = false;
113   /* If true, output only the group ID(s). -g */
114   bool just_group = false;
115   /* If true, output real UID/GID instead of default effective UID/GID. -r */
116   bool use_real = false;
117   /* If true, output only the user ID(s). -u */
118   bool just_user = false;
119
120   initialize_main (&argc, &argv);
121   set_program_name (argv[0]);
122   setlocale (LC_ALL, "");
123   bindtextdomain (PACKAGE, LOCALEDIR);
124   textdomain (PACKAGE);
125
126   atexit (close_stdout);
127
128   while ((optc = getopt_long (argc, argv, "agnruGZ", longopts, NULL)) != -1)
129     {
130       switch (optc)
131         {
132         case 'a':
133           /* Ignore -a, for compatibility with SVR4.  */
134           break;
135
136         case 'Z':
137           /* politely decline if we're not on a selinux-enabled kernel. */
138           if (!selinux_enabled)
139             error (EXIT_FAILURE, 0,
140                    _("--context (-Z) works only on an SELinux-enabled kernel"));
141           just_context = 1;
142           break;
143
144         case 'g':
145           just_group = true;
146           break;
147         case 'n':
148           use_name = true;
149           break;
150         case 'r':
151           use_real = true;
152           break;
153         case 'u':
154           just_user = true;
155           break;
156         case 'G':
157           just_group_list = true;
158           break;
159         case_GETOPT_HELP_CHAR;
160         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
161         default:
162           usage (EXIT_FAILURE);
163         }
164     }
165
166   if (1 < argc - optind)
167     {
168       error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
169       usage (EXIT_FAILURE);
170     }
171
172   if (argc - optind == 1 && just_context)
173     error (EXIT_FAILURE, 0,
174            _("cannot print security context when user specified"));
175
176   /* If we are on a selinux-enabled kernel and no user is specified,
177      get our context. Otherwise, leave the context variable alone -
178      it has been initialized known invalid value and will be not
179      displayed in print_full_info() */
180   if (selinux_enabled && argc == optind)
181     {
182       if (getcon (&context) && just_context)
183         error (EXIT_FAILURE, 0, _("can't get process context"));
184     }
185
186   if (just_user + just_group + just_group_list + just_context > 1)
187     error (EXIT_FAILURE, 0, _("cannot print \"only\" of more than one choice"));
188
189   if (just_user + just_group + just_group_list == 0 && (use_real | use_name))
190     error (EXIT_FAILURE, 0,
191            _("cannot print only names or real IDs in default format"));
192
193   if (argc - optind == 1)
194     {
195       struct passwd *pwd = getpwnam (argv[optind]);
196       if (pwd == NULL)
197         error (EXIT_FAILURE, 0, _("%s: No such user"), argv[optind]);
198       ruid = euid = pwd->pw_uid;
199       rgid = egid = pwd->pw_gid;
200     }
201   else
202     {
203       euid = geteuid ();
204       ruid = getuid ();
205       egid = getegid ();
206       rgid = getgid ();
207     }
208
209   if (just_user)
210     {
211       print_user (use_real ? ruid : euid);
212     }
213   else if (just_group)
214     {
215       if (!print_group (use_real ? rgid : egid, use_name))
216         ok = false;
217     }
218   else if (just_group_list)
219     {
220       if (!print_group_list (argv[optind], ruid, rgid, egid, use_name))
221         ok = false;
222     }
223   else if (just_context)
224     {
225       fputs (context, stdout);
226     }
227   else
228     {
229       print_full_info (argv[optind]);
230     }
231   putchar ('\n');
232
233   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
234 }
235
236 /* Print the name or value of user ID UID. */
237
238 static void
239 print_user (uid_t uid)
240 {
241   struct passwd *pwd = NULL;
242
243   if (use_name)
244     {
245       pwd = getpwuid (uid);
246       if (pwd == NULL)
247         {
248           error (0, 0, _("cannot find name for user ID %lu"),
249                  (unsigned long int) uid);
250           ok = false;
251         }
252     }
253
254   if (pwd == NULL)
255     printf ("%lu", (unsigned long int) uid);
256   else
257     printf ("%s", pwd->pw_name);
258 }
259
260 /* Print all of the info about the user's user and group IDs. */
261
262 static void
263 print_full_info (const char *username)
264 {
265   struct passwd *pwd;
266   struct group *grp;
267
268   printf (_("uid=%lu"), (unsigned long int) ruid);
269   pwd = getpwuid (ruid);
270   if (pwd)
271     printf ("(%s)", pwd->pw_name);
272
273   printf (_(" gid=%lu"), (unsigned long int) rgid);
274   grp = getgrgid (rgid);
275   if (grp)
276     printf ("(%s)", grp->gr_name);
277
278   if (euid != ruid)
279     {
280       printf (_(" euid=%lu"), (unsigned long int) euid);
281       pwd = getpwuid (euid);
282       if (pwd)
283         printf ("(%s)", pwd->pw_name);
284     }
285
286   if (egid != rgid)
287     {
288       printf (_(" egid=%lu"), (unsigned long int) egid);
289       grp = getgrgid (egid);
290       if (grp)
291         printf ("(%s)", grp->gr_name);
292     }
293
294 #if HAVE_GETGROUPS
295   {
296     GETGROUPS_T *groups;
297     size_t i;
298
299     int n_groups = mgetgroups (username, (pwd ? pwd->pw_gid : (gid_t) -1),
300                                &groups);
301     if (n_groups < 0)
302       {
303         if (username)
304           {
305             error (0, errno, _("failed to get groups for user %s"),
306                    quote (username));
307           }
308         else
309           {
310             error (0, errno, _("failed to get groups for the current process"));
311           }
312         ok = false;
313         return;
314       }
315
316     if (n_groups > 0)
317       fputs (_(" groups="), stdout);
318     for (i = 0; i < n_groups; i++)
319       {
320         if (i > 0)
321           putchar (',');
322         printf ("%lu", (unsigned long int) groups[i]);
323         grp = getgrgid (groups[i]);
324         if (grp)
325           printf ("(%s)", grp->gr_name);
326       }
327     free (groups);
328   }
329 #endif /* HAVE_GETGROUPS */
330   if (context != NULL)
331     printf (_(" context=%s"), context);
332 }