Skip to content

Commit 628ae3d

Browse files
committed
su: pass all unknown options
1 parent e729eec commit 628ae3d

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
@@ -130,11 +130,9 @@ pub mod ffi {
130130
struct SuRequest {
131131
target_uid: i32,
132132
target_pid: i32,
133-
login: bool,
134133
keep_env: bool,
135134
drop_cap: bool,
136-
shell: String,
137-
command: String,
135+
command: Vec<String>,
138136
context: String,
139137
gids: Vec<u32>,
140138
}

native/src/core/su/connect.rs

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

250250
fn app_log(&self) {
251-
let command = if self.request.command.is_empty() {
252-
&self.request.shell
253-
} else {
254-
&self.request.command
255-
};
251+
let command = &self.request.command.join(" ");
256252
let extras = [
257253
Extra {
258254
key: "from.uid",

native/src/core/su/daemon.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,14 @@ use std::time::{Duration, Instant};
1414
use std::os::fd::AsRawFd;
1515
use std::sync::nonpoison::Mutex;
1616

17-
const DEFAULT_SHELL: &str = "/system/bin/sh";
18-
1917
impl Default for SuRequest {
2018
fn default() -> Self {
2119
SuRequest {
2220
target_uid: AID_ROOT,
2321
target_pid: -1,
24-
login: false,
2522
keep_env: false,
2623
drop_cap: false,
27-
shell: DEFAULT_SHELL.to_string(),
28-
command: "".to_string(),
24+
command: vec![],
2925
context: "".to_string(),
3026
gids: vec![],
3127
}

native/src/core/su/su.cpp

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

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

@@ -87,10 +87,9 @@ static void setup_sighandlers(void (*handler)(int)) {
8787
}
8888

8989
int su_client_main(int argc, char *argv[]) {
90+
opterr = 0;
9091
option long_opts[] = {
91-
{ "command", required_argument, nullptr, 'c' },
9292
{ "help", no_argument, nullptr, 'h' },
93-
{ "login", no_argument, nullptr, 'l' },
9493
{ "preserve-environment", no_argument, nullptr, 'p' },
9594
{ "shell", required_argument, nullptr, 's' },
9695
{ "version", no_argument, nullptr, 'v' },
@@ -116,29 +115,16 @@ int su_client_main(int argc, char *argv[]) {
116115
}
117116

118117
bool interactive = false;
118+
string shell = DEFAULT_SHELL;
119119

120120
int c;
121-
while ((c = getopt_long(argc, argv, "c:hlimpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
121+
while ((c = getopt_long(argc, argv, "+:himpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
122122
switch (c) {
123-
case 'c': {
124-
string command;
125-
for (int i = optind - 1; i < argc; ++i) {
126-
if (!command.empty())
127-
command += ' ';
128-
command += argv[i];
129-
}
130-
req.command = command;
131-
optind = argc;
132-
break;
133-
}
134123
case 'h':
135124
usage(EXIT_SUCCESS);
136125
case 'i':
137126
interactive = true;
138127
break;
139-
case 'l':
140-
req.login = true;
141-
break;
142128
case 'm':
143129
case 'p':
144130
req.keep_env = true;
@@ -147,7 +133,7 @@ int su_client_main(int argc, char *argv[]) {
147133
req.drop_cap = true;
148134
break;
149135
case 's':
150-
req.shell = optarg;
136+
shell = optarg;
151137
break;
152138
case 'V':
153139
printf("%d\n", MAGISK_VER_CODE);
@@ -176,34 +162,46 @@ int su_client_main(int argc, char *argv[]) {
176162
break;
177163
case 'g':
178164
case 'G': {
179-
vector<gid_t> gids;
180165
if (int gid = parse_int(optarg); gid >= 0) {
181-
gids.insert(c == 'g' ? gids.begin() : gids.end(), gid);
166+
if (c == 'g' && !req.gids.empty()) {
167+
req.gids.push_back(req.gids[0]);
168+
req.gids[0] = gid;
169+
} else {
170+
req.gids.push_back(gid);
171+
}
182172
} else {
183173
fprintf(stderr, "Invalid GID: %s\n", optarg);
184174
usage(EXIT_FAILURE);
185175
}
186-
ranges::copy(gids, std::back_inserter(req.gids));
187176
break;
188177
}
189-
default:
190-
/* Bionic getopt_long doesn't terminate its error output by newline */
191-
fprintf(stderr, "\n");
178+
case ':':
179+
fprintf(stderr, "option '%s' requires an argument\n", argv[optind - 1]);
192180
usage(2);
181+
case '?':
182+
optind--;
183+
goto end;
193184
}
194185
}
195186

196-
if (optind < argc && strcmp(argv[optind], "-") == 0) {
197-
req.login = true;
198-
optind++;
199-
}
187+
end:
200188
/* username or uid */
201189
if (optind < argc) {
202-
if (const passwd *pw = getpwnam(argv[optind]))
190+
if (const passwd *pw = getpwnam(argv[optind])) {
203191
req.target_uid = pw->pw_uid;
204-
else
205-
req.target_uid = parse_int(argv[optind]);
206-
optind++;
192+
optind++;
193+
} else if (int uid = parse_int(argv[optind]); uid >= 0) {
194+
req.target_uid = uid;
195+
optind++;
196+
}
197+
}
198+
199+
req.command.emplace_back(shell.c_str());
200+
if (optind < argc) {
201+
for (int i = optind; i < argc; ++i) {
202+
req.command.push_back(argv[i]);
203+
}
204+
optind = argc;
207205
}
208206

209207
// Connect to client
@@ -220,7 +218,7 @@ int su_client_main(int argc, char *argv[]) {
220218
}
221219

222220
// Determine which one of our streams are attached to a TTY
223-
interactive |= req.command.empty();
221+
interactive |= req.command.size() == 1;
224222
int atty = 0;
225223
if (isatty(STDIN_FILENO) && interactive) atty |= ATTY_IN;
226224
if (isatty(STDOUT_FILENO) && interactive) atty |= ATTY_OUT;
@@ -407,14 +405,11 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
407405
break;
408406
}
409407

410-
const char *argv[4] = { nullptr };
411-
412-
argv[0] = req.login ? "-" : req.shell.c_str();
413-
414-
if (!req.command.empty()) {
415-
argv[1] = "-c";
416-
argv[2] = req.command.c_str();
408+
vector<const char *> argv;
409+
for (auto &str: req.command) {
410+
argv.push_back(str.c_str());
417411
}
412+
argv.push_back(nullptr);
418413

419414
// Setup environment
420415
umask(022);
@@ -439,7 +434,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
439434
setenv("HOME", pw->pw_dir, 1);
440435
setenv("USER", pw->pw_name, 1);
441436
setenv("LOGNAME", pw->pw_name, 1);
442-
setenv("SHELL", req.shell.c_str(), 1);
437+
setenv("SHELL", argv[0], 1);
443438
}
444439
}
445440

@@ -458,7 +453,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
458453
sigemptyset(&block_set);
459454
sigprocmask(SIG_SETMASK, &block_set, nullptr);
460455

461-
execvp(req.shell.c_str(), (char **) argv);
462-
fprintf(stderr, "Cannot execute %s: %s\n", req.shell.c_str(), strerror(errno));
456+
execvp(argv[0], const_cast<char* const*>(argv.data()));
457+
fprintf(stderr, "Cannot execute %s: %s\n", argv[0], strerror(errno));
463458
PLOGE("exec");
464459
}

0 commit comments

Comments
 (0)