1 /* chroot -- run command or shell with special root directory
2 Copyright (C) 95, 96, 1997, 1999-2004, 2007-2009
3 Free Software Foundation, Inc.
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 3 of the License, or
8 (at your option) any later version.
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, see <http://www.gnu.org/licenses/>. */
18 /* Written by Roland McGrath. */
23 #include <sys/types.h>
28 #include "long-options.h"
33 /* The official name of this program (e.g., no `g' prefix). */
34 #define PROGRAM_NAME "chroot"
36 #define AUTHORS proper_name ("Roland McGrath")
39 # define MAXGID GID_T_MAX
44 GROUPS
= UCHAR_MAX
+ 1,
48 static struct option
const long_opts
[] =
50 {"groups", required_argument
, NULL
, GROUPS
},
51 {"userspec", required_argument
, NULL
, USERSPEC
},
52 {GETOPT_HELP_OPTION_DECL
},
53 {GETOPT_VERSION_OPTION_DECL
},
57 /* Groups is a comma separated list of additional groups. */
59 set_additional_groups (char const *groups
)
61 GETGROUPS_T
*gids
= NULL
;
62 size_t n_gids_allocated
= 0;
64 char *buffer
= xstrdup (groups
);
68 for (tmp
= strtok (buffer
, ","); tmp
; tmp
= strtok (NULL
, ","))
71 unsigned long int value
;
73 if (xstrtoul (tmp
, NULL
, 10, &value
, "") == LONGINT_OK
&& value
<= MAXGID
)
86 error (0, errno
, _("cannot find group %s"), tmp
);
91 if (n_gids
== n_gids_allocated
)
92 gids
= x2nrealloc (gids
, &n_gids_allocated
, sizeof *gids
);
93 gids
[n_gids
++] = value
;
98 ret
= setgroups (n_gids
, gids
);
109 if (status
!= EXIT_SUCCESS
)
110 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
115 Usage: %s [OPTION] NEWROOT [COMMAND [ARG]...]\n\
117 "), program_name
, program_name
);
120 Run COMMAND with root directory set to NEWROOT.\n\
125 --userspec=USER:GROUP specify user and group (ID or name) to use\n\
126 --groups=G_LIST specify supplementary groups as g1,g2,..,gN\n\
129 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
130 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
133 If no command is given, run ``${SHELL} -i'' (default: /bin/sh).\n\
135 emit_bug_reporting_address ();
141 main (int argc
, char **argv
)
144 char const *userspec
= NULL
;
145 char const *groups
= NULL
;
147 initialize_main (&argc
, &argv
);
148 set_program_name (argv
[0]);
149 setlocale (LC_ALL
, "");
150 bindtextdomain (PACKAGE
, LOCALEDIR
);
151 textdomain (PACKAGE
);
153 initialize_exit_failure (EXIT_FAILURE
);
154 atexit (close_stdout
);
156 parse_long_options (argc
, argv
, PROGRAM_NAME
, PACKAGE_NAME
, Version
,
157 usage
, AUTHORS
, (char const *) NULL
);
159 while ((c
= getopt_long (argc
, argv
, "+", long_opts
, NULL
)) != -1)
170 usage (EXIT_FAILURE
);
176 error (0, 0, _("missing operand"));
177 usage (EXIT_FAILURE
);
180 if (chroot (argv
[optind
]) != 0)
181 error (EXIT_FAILURE
, errno
, _("cannot change root directory to %s"),
185 error (EXIT_FAILURE
, errno
, _("cannot chdir to root directory"));
187 if (argc
== optind
+ 1)
189 /* No command. Run an interactive shell. */
190 char *shell
= getenv ("SHELL");
192 shell
= bad_cast ("/bin/sh");
194 argv
[1] = bad_cast ("-i");
199 /* The following arguments give the command. */
209 char const *err
= parse_user_spec (userspec
, &uid
, &gid
, &user
, &group
);
213 error (EXIT_FAILURE
, errno
, "%s", err
);
218 /* Attempt to set all three: supplementary groups, group ID, user ID.
219 Diagnose any failures. If any have failed, exit before execvp. */
220 if (groups
&& set_additional_groups (groups
))
222 error (0, errno
, _("failed to set additional groups"));
226 if (gid
!= (gid_t
) -1 && setgid (gid
))
228 error (0, errno
, _("failed to set group-ID"));
232 if (uid
!= (uid_t
) -1 && setuid (uid
))
234 error (0, errno
, _("failed to set user-ID"));
242 /* Execute the given command. */
243 execvp (argv
[0], argv
);
246 int exit_status
= (errno
== ENOENT
? EXIT_ENOENT
: EXIT_CANNOT_INVOKE
);
247 error (0, errno
, _("cannot run command %s"), quote (argv
[0]));