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