2828#include <fcntl.h>
2929#include <errno.h>
3030#include <endian.h>
31-
3231#include <stdio.h>
3332#include <stdlib.h>
3433#include <string.h>
35-
34+ #include <getopt.h>
3635#include <stdint.h>
3736#include <pwd.h>
3837
@@ -253,23 +252,22 @@ static int socket_receive_result(int fd, char *result, ssize_t result_len)
253252 return 0 ;
254253}
255254
256- static void usage (void )
255+ static void usage (int status )
257256{
258- printf ("Usage: su [options] [LOGIN]\n\n" );
259- printf ("Options:\n" );
260- printf (" -c, --command COMMAND pass COMMAND to the invoked shell\n" );
261- printf (" -h, --help display this help message and exit\n" );
262- printf (" -, -l, --login make the shell a login shell\n" );
263- // I'll look more into this to figure out what it's about,
264- // maybe implement it later
265- // printf(" -m, -p,\n");
266- // printf(" --preserve-environment do not reset environment variables, and\n");
267- // printf(" keep the same shell\n");
268- printf (" -s, --shell SHELL use SHELL instead of the default in passwd\n" );
269- printf (" -v, --version display version number and exit\n" );
270- printf (" -V display version code and exit. this is\n" );
271- printf (" used almost exclusively by Superuser.apk\n" );
272- exit (EXIT_SUCCESS );
257+ FILE * stream = (status == EXIT_SUCCESS ) ? stdout : stderr ;
258+
259+ fprintf (stream ,
260+ "Usage: su [options] [LOGIN]\n\n"
261+ "Options:\n"
262+ " -c, --command COMMAND pass COMMAND to the invoked shell\n"
263+ " -h, --help display this help message and exit\n"
264+ " -, -l, --login, -m, -p,\n"
265+ " --preserve-environment do nothing, kept for compatibility\n"
266+ " -s, --shell SHELL use SHELL instead of the default " DEFAULT_COMMAND "\n"
267+ " -v, --version display version number and exit\n"
268+ " -V display version code and exit,\n"
269+ " this is used almost exclusively by Superuser.apk\n" );
270+ exit (status );
273271}
274272
275273static void deny (void )
@@ -293,8 +291,8 @@ static void allow(char *shell, mode_t mask)
293291 umask (mask );
294292 send_intent (& su_from , & su_to , "" , 1 , 1 );
295293
296- if (!strcmp ( shell , "" ) ) {
297- strcpy ( shell , DEFAULT_COMMAND ) ;
294+ if (!shell ) {
295+ shell = DEFAULT_COMMAND ;
298296 }
299297 exe = strrchr (shell , '/' );
300298 exe = (exe ) ? exe + 1 : shell ;
@@ -317,48 +315,61 @@ int main(int argc, char *argv[])
317315{
318316 struct stat st ;
319317 int socket_serv_fd , fd ;
320- char buf [64 ], shell [ PATH_MAX ] , * result ;
321- int i , dballow ;
318+ char buf [64 ], * shell = NULL , * result ;
319+ int c , dballow ;
322320 mode_t orig_umask ;
323-
324- for (i = 1 ; i < argc ; i ++ ) {
325- if (!strcmp (argv [i ], "-c" ) || !strcmp (argv [i ], "--command" )) {
326- if (++ i < argc ) {
327- su_to .command = argv [i ];
328- } else {
329- usage ();
330- }
331- } else if (!strcmp (argv [i ], "-s" ) || !strcmp (argv [i ], "--shell" )) {
332- if (++ i < argc ) {
333- strncpy (shell , argv [i ], sizeof (shell ));
334- shell [sizeof (shell ) - 1 ] = 0 ;
335- } else {
336- usage ();
337- }
338- } else if (!strcmp (argv [i ], "-v" ) || !strcmp (argv [i ], "--version" )) {
339- printf ("%s\n" , VERSION );
340- exit (EXIT_SUCCESS );
341- } else if (!strcmp (argv [i ], "-V" )) {
342- printf ("%d\n" , VERSION_CODE );
343- exit (EXIT_SUCCESS );
344- } else if (!strcmp (argv [i ], "-h" ) || !strcmp (argv [i ], "--help" )) {
345- usage ();
346- } else if (!strcmp (argv [i ], "-" ) || !strcmp (argv [i ], "-l" ) ||
347- !strcmp (argv [i ], "--login" )) {
348- ++ i ;
321+ struct option long_opts [] = {
322+ { "command" , required_argument , NULL , 'c' },
323+ { "help" , no_argument , NULL , 'h' },
324+ { "login" , no_argument , NULL , 'l' },
325+ { "preserve-environment" , no_argument , NULL , 'p' },
326+ { "shell" , required_argument , NULL , 's' },
327+ { "version" , no_argument , NULL , 'v' },
328+ { NULL , 0 , NULL , 0 },
329+ };
330+
331+ while ((c = getopt_long (argc , argv , "c:hlmps:Vv" , long_opts , NULL )) != -1 ) {
332+ switch (c ) {
333+ case 'c' :
334+ su_to .command = optarg ;
349335 break ;
350- } else {
336+ case 'h' :
337+ usage (EXIT_SUCCESS );
338+ break ;
339+ case 'l' : /* for compatibility */
340+ case 'm' :
341+ case 'p' :
342+ break ;
343+ case 's' :
344+ shell = optarg ;
351345 break ;
346+ case 'V' :
347+ printf ("%d\n" , VERSION_CODE );
348+ exit (EXIT_SUCCESS );
349+ case 'v' :
350+ printf ("%s\n" , VERSION );
351+ exit (EXIT_SUCCESS );
352+ default :
353+ /* Bionic getopt_long doesn't terminate its error output by newline */
354+ fprintf (stderr , "\n" );
355+ usage (2 );
352356 }
353357 }
354- if (i < argc - 1 ) {
355- usage ();
358+ if (optind < argc && !strcmp (argv [optind ], "-" )) {
359+ optind ++ ;
360+ }
361+ /*
362+ * Other su implementations pass the remaining args to the shell.
363+ * -- maybe implement this later
364+ */
365+ if (optind < argc - 1 ) {
366+ usage (2 );
356367 }
357- if (i == argc - 1 ) {
368+ if (optind == argc - 1 ) {
358369 struct passwd * pw ;
359- pw = getpwnam (argv [i ]);
370+ pw = getpwnam (argv [optind ]);
360371 if (!pw ) {
361- su_to .uid = atoi (argv [i ]);
372+ su_to .uid = atoi (argv [optind ]);
362373 } else {
363374 su_to .uid = pw -> pw_uid ;
364375 }
0 commit comments