Skip to content

Commit 21cc745

Browse files
committed
su: pass all unknown options
1 parent 9476e72 commit 21cc745

5 files changed

Lines changed: 60 additions & 73 deletions

File tree

docs/tools.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -281,25 +281,27 @@ Actions:
281281
An applet of `magisk`, the MagiskSU entry point. Good old `su` command.
282282

283283
```
284-
Usage: su [options] [-] [user [argument...]]
284+
Usage: su [options] [--] [user [argument...]]
285285
286286
Options:
287-
-c, --command COMMAND Pass COMMAND to the invoked shell
287+
-s, --shell SHELL Use SHELL instead of the default /system/bin/sh
288+
-i, --interactive Force pseudo-terminal allocation
288289
-g, --group GROUP Specify the primary group
289-
-G, --supp-group GROUP Specify a supplementary group.
290+
-G, --supp-group GROUP Specify a supplementary group
290291
The first specified supplementary group is also used
291-
as a primary group if the option -g is not specified.
292+
as a primary group if the option -g is not specified
292293
-Z, --context CONTEXT Change SELinux context
293294
-t, --target PID PID to take mount namespace from
294-
-h, --help Display this help message and exit
295-
-, -l, --login Pretend the shell to be a login shell
295+
pid 0 means magisk global mount namespacen
296296
-m, -p,
297297
--preserve-environment Preserve the entire environment
298-
-s, --shell SHELL Use SHELL instead of the default /system/bin/sh
299298
-v, --version Display version number and exit
300299
-V Display version code and exit
301-
-mm, -M,
302-
--mount-master Force run in the global mount namespace
300+
-h, --help Display this help message and exit
301+
302+
--: Force stop options parsing, and also stop when an unknown option is found
303+
User: The user to switch to (default root), it can be name or uid
304+
Argument: Pass it to the shell as is
303305
```
304306

305307
### resetprop

native/src/core/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,9 @@ pub mod ffi {
116116
struct SuRequest {
117117
target_uid: i32,
118118
target_pid: i32,
119-
login: bool,
120119
keep_env: bool,
121120
drop_cap: bool,
122-
shell: String,
123-
command: String,
121+
command: Vec<String>,
124122
context: String,
125123
gids: Vec<u32>,
126124
}

native/src/core/su/connect.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,7 @@ impl SuAppContext<'_> {
243243
}
244244

245245
fn app_log(&self) {
246-
let command = if self.request.command.is_empty() {
247-
&self.request.shell
248-
} else {
249-
&self.request.command
250-
};
246+
let command = &self.request.command.join(" ");
251247
let extras = [
252248
Extra {
253249
key: "from.uid",

native/src/core/su/daemon.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,14 @@ use std::os::unix::net::UnixStream;
1111
use std::sync::{Arc, Mutex};
1212
use std::time::{Duration, Instant};
1313

14-
const DEFAULT_SHELL: &str = "/system/bin/sh";
15-
1614
impl Default for SuRequest {
1715
fn default() -> Self {
1816
SuRequest {
1917
target_uid: AID_ROOT,
2018
target_pid: -1,
21-
login: false,
2219
keep_env: false,
2320
drop_cap: false,
24-
shell: DEFAULT_SHELL.to_string(),
25-
command: "".to_string(),
21+
command: vec![],
2622
context: "".to_string(),
2723
gids: vec![],
2824
}

native/src/core/su/su.cpp

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,26 @@ int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGI
3939

4040
fprintf(stream,
4141
"MagiskSU\n\n"
42-
"Usage: su [options] [-] [user [argument...]]\n\n"
42+
"Usage: su [options] [--] [user [argument...]]\n\n"
4343
"Options:\n"
44-
" -c, --command COMMAND Pass COMMAND to the invoked shell\n"
45-
" -i, --interactive Force pseudo-terminal allocation when using -c\n"
44+
" -s, --shell SHELL Use SHELL instead of the default " DEFAULT_SHELL "\n"
45+
" -i, --interactive Force pseudo-terminal allocation\n"
4646
" -g, --group GROUP Specify the primary group\n"
4747
" -G, --supp-group GROUP Specify a supplementary group\n"
4848
" The first specified supplementary group is also used\n"
4949
" as a primary group if the option -g is not specified\n"
5050
" -Z, --context CONTEXT Change SELinux context\n"
5151
" -t, --target PID PID to take mount namespace from\n"
52+
" pid 0 means magisk global mount namespace\n"
5253
" -d, --drop-cap Drop all Linux capabilities\n"
53-
" -h, --help Display this help message and exit\n"
54-
" -, -l, --login Pretend the shell to be a login shell\n"
5554
" -m, -p,\n"
5655
" --preserve-environment Preserve the entire environment\n"
57-
" -s, --shell SHELL Use SHELL instead of the default " DEFAULT_SHELL "\n"
5856
" -v, --version Display version number and exit\n"
5957
" -V Display version code and exit\n"
60-
" -mm, -M,\n"
61-
" --mount-master Force run in the global mount namespace\n\n");
58+
" -h, --help Display this help message and exit\n\n"
59+
"--: Force stop options parsing, and also stop when an unknown option is found\n"
60+
"User: The user to switch to (default root), it can be name or uid\n"
61+
"Argument: Pass it to the shell as is\n\n");
6262
exit(status);
6363
}
6464

@@ -96,10 +96,9 @@ static void setup_sighandlers(void (*handler)(int)) {
9696

9797
int su_client_main(int argc, char *argv[]) {
9898
int c;
99+
opterr = 0;
99100
struct option long_opts[] = {
100-
{ "command", required_argument, nullptr, 'c' },
101101
{ "help", no_argument, nullptr, 'h' },
102-
{ "login", no_argument, nullptr, 'l' },
103102
{ "preserve-environment", no_argument, nullptr, 'p' },
104103
{ "shell", required_argument, nullptr, 's' },
105104
{ "version", no_argument, nullptr, 'v' },
@@ -125,28 +124,15 @@ int su_client_main(int argc, char *argv[]) {
125124
}
126125

127126
bool interactive = false;
127+
string shell = DEFAULT_SHELL;
128128

129-
while ((c = getopt_long(argc, argv, "c:hlimpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
129+
while ((c = getopt_long(argc, argv, "+:himpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
130130
switch (c) {
131-
case 'c': {
132-
string command;
133-
for (int i = optind - 1; i < argc; ++i) {
134-
if (!command.empty())
135-
command += ' ';
136-
command += argv[i];
137-
}
138-
req.command = command;
139-
optind = argc;
140-
break;
141-
}
142131
case 'h':
143132
usage(EXIT_SUCCESS);
144133
case 'i':
145134
interactive = true;
146135
break;
147-
case 'l':
148-
req.login = true;
149-
break;
150136
case 'm':
151137
case 'p':
152138
req.keep_env = true;
@@ -155,7 +141,7 @@ int su_client_main(int argc, char *argv[]) {
155141
req.drop_cap = true;
156142
break;
157143
case 's':
158-
req.shell = optarg;
144+
shell = optarg;
159145
break;
160146
case 'V':
161147
printf("%d\n", MAGISK_VER_CODE);
@@ -184,36 +170,48 @@ int su_client_main(int argc, char *argv[]) {
184170
break;
185171
case 'g':
186172
case 'G': {
187-
vector<gid_t> gids;
188173
if (int gid = parse_int(optarg); gid >= 0) {
189-
gids.insert(c == 'g' ? gids.begin() : gids.end(), gid);
174+
if (c == 'g' && !req.gids.empty()) {
175+
req.gids.push_back(req.gids[0]);
176+
req.gids[0] = gid;
177+
} else {
178+
req.gids.push_back(gid);
179+
}
190180
} else {
191181
fprintf(stderr, "Invalid GID: %s\n", optarg);
192182
usage(EXIT_FAILURE);
193183
}
194-
std::copy(gids.begin(), gids.end(), std::back_inserter(req.gids));
195184
break;
196185
}
197-
default:
198-
/* Bionic getopt_long doesn't terminate its error output by newline */
199-
fprintf(stderr, "\n");
186+
case ':':
187+
fprintf(stderr, "option '%s' requires an argument\n", argv[optind - 1]);
200188
usage(2);
189+
case '?':
190+
optind--;
191+
goto end;
201192
}
202193
}
203194

204-
if (optind < argc && strcmp(argv[optind], "-") == 0) {
205-
req.login = true;
206-
optind++;
207-
}
195+
end:
208196
/* username or uid */
209197
if (optind < argc) {
210198
struct passwd *pw;
211199
pw = getpwnam(argv[optind]);
212-
if (pw)
200+
if (pw) {
213201
req.target_uid = pw->pw_uid;
214-
else
215-
req.target_uid = parse_int(argv[optind]);
216-
optind++;
202+
optind++;
203+
} else if (int uid = parse_int(argv[optind]); uid >= 0) {
204+
req.target_uid = uid;
205+
optind++;
206+
}
207+
}
208+
209+
req.command.emplace_back(shell.c_str());
210+
if (optind < argc) {
211+
for (int i = optind; i < argc; ++i) {
212+
req.command.push_back(argv[i]);
213+
}
214+
optind = argc;
217215
}
218216

219217
int ptmx, fd;
@@ -232,7 +230,7 @@ int su_client_main(int argc, char *argv[]) {
232230
}
233231

234232
// Determine which one of our streams are attached to a TTY
235-
interactive |= req.command.empty();
233+
interactive |= req.command.size() == 1;
236234
int atty = 0;
237235
if (isatty(STDIN_FILENO) && interactive) atty |= ATTY_IN;
238236
if (isatty(STDOUT_FILENO) && interactive) atty |= ATTY_OUT;
@@ -431,14 +429,11 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
431429
break;
432430
}
433431

434-
const char *argv[4] = { nullptr };
435-
436-
argv[0] = req.login ? "-" : req.shell.c_str();
437-
438-
if (!req.command.empty()) {
439-
argv[1] = "-c";
440-
argv[2] = req.command.c_str();
432+
vector<const char *> argv;
433+
for (auto &str: req.command) {
434+
argv.push_back(str.c_str());
441435
}
436+
argv.push_back(nullptr);
442437

443438
// Setup environment
444439
umask(022);
@@ -463,7 +458,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
463458
setenv("HOME", pw->pw_dir, 1);
464459
setenv("USER", pw->pw_name, 1);
465460
setenv("LOGNAME", pw->pw_name, 1);
466-
setenv("SHELL", req.shell.c_str(), 1);
461+
setenv("SHELL", argv[0], 1);
467462
}
468463
}
469464

@@ -482,7 +477,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
482477
sigemptyset(&block_set);
483478
sigprocmask(SIG_SETMASK, &block_set, nullptr);
484479

485-
execvp(req.shell.c_str(), (char **) argv);
486-
fprintf(stderr, "Cannot execute %s: %s\n", req.shell.c_str(), strerror(errno));
480+
execvp(argv[0], const_cast<char* const*>(argv.data()));
481+
fprintf(stderr, "Cannot execute %s: %s\n", argv[0], strerror(errno));
487482
PLOGE("exec");
488483
}

0 commit comments

Comments
 (0)