@@ -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
9797int 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