@@ -38,23 +38,20 @@ int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGI
3838 " MagiskSU\n\n "
3939 " Usage: su [options] [-] [user [argument...]]\n\n "
4040 " Options:\n "
41- " -c , --command COMMAND Pass COMMAND to the invoked shell \n "
42- " -i, --interactive Force pseudo-terminal allocation when using -c \n "
41+ " -s , --shell SHELL Use SHELL instead of the default " DEFAULT_SHELL " \n "
42+ " -i, --interactive Force pseudo-terminal allocation\n "
4343 " -g, --group GROUP Specify the primary group\n "
4444 " -G, --supp-group GROUP Specify a supplementary group\n "
4545 " The first specified supplementary group is also used\n "
4646 " as a primary group if the option -g is not specified\n "
4747 " -Z, --context CONTEXT Change SELinux context\n "
4848 " -t, --target PID PID to take mount namespace from\n "
49- " -h, --help Display this help message and exit\n "
50- " -, -l, --login Pretend the shell to be a login shell\n "
49+ " pid 0 means magisk global mount namespace"
5150 " -m, -p,\n "
5251 " --preserve-environment Preserve the entire environment\n "
53- " -s, --shell SHELL Use SHELL instead of the default " DEFAULT_SHELL " \n "
5452 " -v, --version Display version number and exit\n "
5553 " -V Display version code and exit\n "
56- " -mm, -M,\n "
57- " --mount-master Force run in the global mount namespace\n\n " );
54+ " -h, --help Display this help message and exit\n\n " );
5855 exit (status);
5956}
6057
@@ -92,10 +89,9 @@ static void setup_sighandlers(void (*handler)(int)) {
9289
9390int su_client_main (int argc, char *argv[]) {
9491 int c;
92+ opterr = 0 ;
9593 struct option long_opts[] = {
96- { " command" , required_argument, nullptr , ' c' },
9794 { " help" , no_argument, nullptr , ' h' },
98- { " login" , no_argument, nullptr , ' l' },
9995 { " preserve-environment" , no_argument, nullptr , ' p' },
10096 { " shell" , required_argument, nullptr , ' s' },
10197 { " version" , no_argument, nullptr , ' v' },
@@ -120,34 +116,21 @@ int su_client_main(int argc, char *argv[]) {
120116 }
121117
122118 bool interactive = false ;
119+ string shell = DEFAULT_SHELL ;
123120
124- while ((c = getopt_long (argc, argv, " c:hlimps :VvuZ:Mt:g:G:" , long_opts, nullptr )) != -1 ) {
121+ while ((c = getopt_long (argc, argv, " +:himps :VvuZ:Mt:g:G:" , long_opts, nullptr )) != -1 ) {
125122 switch (c) {
126- case ' c' : {
127- string command;
128- for (int i = optind - 1 ; i < argc; ++i) {
129- if (!command.empty ())
130- command += ' ' ;
131- command += argv[i];
132- }
133- req.command = command;
134- optind = argc;
135- break ;
136- }
137123 case ' h' :
138124 usage (EXIT_SUCCESS );
139125 case ' i' :
140126 interactive = true ;
141127 break ;
142- case ' l' :
143- req.login = true ;
144- break ;
145128 case ' m' :
146129 case ' p' :
147130 req.keep_env = true ;
148131 break ;
149132 case ' s' :
150- req. shell = optarg;
133+ shell = optarg;
151134 break ;
152135 case ' V' :
153136 printf (" %d\n " , MAGISK_VER_CODE );
@@ -176,36 +159,48 @@ int su_client_main(int argc, char *argv[]) {
176159 break ;
177160 case ' g' :
178161 case ' G' : {
179- vector<gid_t > gids;
180162 if (int gid = parse_int (optarg); gid >= 0 ) {
181- gids.insert (c == ' g' ? gids.begin () : gids.end (), gid);
163+ if (c == ' g' && !req.gids .empty ()) {
164+ req.gids .push_back (req.gids [0 ]);
165+ req.gids [0 ] = gid;
166+ } else {
167+ req.gids .push_back (gid);
168+ }
182169 } else {
183170 fprintf (stderr, " Invalid GID: %s\n " , optarg);
184171 usage (EXIT_FAILURE );
185172 }
186- std::copy (gids.begin (), gids.end (), std::back_inserter (req.gids ));
187173 break ;
188174 }
189- default :
190- /* Bionic getopt_long doesn't terminate its error output by newline */
191- fprintf (stderr, " \n " );
175+ case ' :' :
176+ fprintf (stderr, " option '%s' requires an argument\n " , argv[optind - 1 ]);
192177 usage (2 );
178+ case ' ?' :
179+ optind--;
180+ goto end;
193181 }
194182 }
195183
196- if (optind < argc && strcmp (argv[optind], " -" ) == 0 ) {
197- req.login = true ;
198- optind++;
199- }
184+ end:
200185 /* username or uid */
201186 if (optind < argc) {
202187 struct passwd *pw;
203188 pw = getpwnam (argv[optind]);
204- if (pw)
189+ if (pw) {
205190 req.target_uid = pw->pw_uid ;
206- else
207- req.target_uid = parse_int (argv[optind]);
208- optind++;
191+ optind++;
192+ } else if (int uid = parse_int (argv[optind]); uid >= 0 ) {
193+ req.target_uid = uid;
194+ optind++;
195+ }
196+ }
197+
198+ req.command .emplace_back (shell.c_str ());
199+ if (optind < argc) {
200+ for (int i = optind; i < argc; ++i) {
201+ req.command .push_back (argv[i]);
202+ }
203+ optind = argc;
209204 }
210205
211206 int ptmx, fd;
@@ -224,7 +219,7 @@ int su_client_main(int argc, char *argv[]) {
224219 }
225220
226221 // Determine which one of our streams are attached to a TTY
227- interactive |= req.command .empty () ;
222+ interactive |= req.command .size () == 1 ;
228223 int atty = 0 ;
229224 if (isatty (STDIN_FILENO ) && interactive) atty |= ATTY_IN ;
230225 if (isatty (STDOUT_FILENO ) && interactive) atty |= ATTY_OUT ;
@@ -368,14 +363,11 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
368363 break ;
369364 }
370365
371- const char *argv[4 ] = { nullptr };
372-
373- argv[0 ] = req.login ? " -" : req.shell .c_str ();
374-
375- if (!req.command .empty ()) {
376- argv[1 ] = " -c" ;
377- argv[2 ] = req.command .c_str ();
366+ vector<const char *> argv;
367+ for (auto &str: req.command ) {
368+ argv.push_back (str.c_str ());
378369 }
370+ argv.push_back (nullptr );
379371
380372 // Setup environment
381373 umask (022 );
@@ -400,7 +392,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
400392 setenv (" HOME" , pw->pw_dir , 1 );
401393 setenv (" USER" , pw->pw_name , 1 );
402394 setenv (" LOGNAME" , pw->pw_name , 1 );
403- setenv (" SHELL" , req. shell . c_str () , 1 );
395+ setenv (" SHELL" , argv[ 0 ] , 1 );
404396 }
405397 }
406398
@@ -413,7 +405,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
413405 if (f) fprintf (f.get (), " %s" , req.context .c_str ());
414406 }
415407 set_identity (req.target_uid , req.gids );
416- execvp (req. shell . c_str (), ( char **) argv);
417- fprintf (stderr, " Cannot execute %s: %s\n " , req. shell . c_str () , strerror (errno));
408+ execvp (argv[ 0 ], const_cast < char * const *>( argv. data ()) );
409+ fprintf (stderr, " Cannot execute %s: %s\n " , argv[ 0 ] , strerror (errno));
418410 PLOGE (" exec" );
419411}
0 commit comments