Skip to content

Commit fc7479f

Browse files
author
git-core
committed
Be compatible with other implementation of su
o use getopt_long(3), so -l, --login can be allowed anywhere in the argument list o add -m and -p which do nothing o su -h outputs usage in stdout and exits with 0 while su with invalid options outputs usage in stderr and exits with 2 now
1 parent da86864 commit fc7479f

File tree

1 file changed

+65
-54
lines changed

1 file changed

+65
-54
lines changed

su.c

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@
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

275273
static 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

Comments
 (0)