Use getopt() to parse the command line. We need to be able to

intersperse env variables and options yet still honor "--""
which complicates things slightly.
This commit is contained in:
Todd C. Miller
2008-03-12 23:42:32 +00:00
parent 299fe04af0
commit d11cb3d14b

153
sudo.c
View File

@@ -157,6 +157,10 @@ static char *runas_user;
static char *runas_group;
static struct sudo_nss_list *snl;
/* For getopt(3) */
extern char *optarg;
extern int optind;
int
main(argc, argv, envp)
int argc;
@@ -803,7 +807,9 @@ set_cmnd(sudo_mode)
}
/*
* Command line argument parsing, can't use getopt(3) due to optional args.
* Command line argument parsing.
* Sets NewArgc and NewArgv which corresponds to the argc/argv we'll use
* for the command to be run (if we are running one).
*/
static int
parse_args(argc, argv)
@@ -812,9 +818,7 @@ parse_args(argc, argv)
{
int rval = MODE_RUN; /* what mode is sudo to be run in? */
int excl = 0; /* exclusive arg, no others allowed */
NewArgv = argv + 1;
NewArgc = argc - 1;
int ch;
/* First, check to see if we were invoked as "sudoedit". */
if (strcmp(getprogname(), "sudoedit") == 0) {
@@ -823,83 +827,51 @@ parse_args(argc, argv)
} else
rval = MODE_RUN;
while (NewArgc > 0) {
if (NewArgv[0][0] == '-') {
if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0' &&
NewArgv[0][2] != 'l') {
warningx("please use single character options");
usage(1);
}
/* Returns true if the last option string was "--" */
#define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
switch (NewArgv[0][1]) {
/* Returns true if next option is an environment variable */
#define is_envar (optind < argc && argv[optind][0] != '/' && \
strchr(argv[optind], '=') != NULL)
for (;;) {
/*
* We disable arg permutation for GNU getopt().
* Some trickiness is required to allow environment variables
* to be interspersed with command line options.
*/
if ((ch = getopt(argc, argv, "+Aa:bC:c:Eeg:HhiKkLlPp:r:Sst:Uu:Vv")) != -1) {
switch (ch) {
case 'A':
SET(tgetpass_flags, TGP_ASKPASS);
break;
case 'p':
/* Must have an associated prompt. */
if (NewArgv[1] == NULL)
usage(1);
user_prompt = NewArgv[1];
user_prompt = optarg;
def_passprompt_override = TRUE;
NewArgc--;
NewArgv++;
break;
case 'u':
/* Must have an associated runas user. */
if (NewArgv[1] == NULL)
usage(1);
runas_user = NewArgv[1];
NewArgc--;
NewArgv++;
runas_user = optarg;
break;
case 'g':
/* Must have an associated runas group. */
if (NewArgv[1] == NULL)
usage(1);
runas_group = NewArgv[1];
NewArgc--;
NewArgv++;
runas_group = optarg;
break;
#ifdef HAVE_BSD_AUTH_H
case 'a':
/* Must have an associated authentication style. */
if (NewArgv[1] == NULL)
usage(1);
login_style = NewArgv[1];
NewArgc--;
NewArgv++;
login_style = optarg;
break;
#endif
#ifdef HAVE_LOGIN_CAP_H
case 'c':
/* Must have an associated login class. */
if (NewArgv[1] == NULL)
usage(1);
login_class = NewArgv[1];
login_class = optarg;
def_use_loginclass = TRUE;
NewArgc--;
NewArgv++;
break;
#endif
case 'C':
if (NewArgv[1] == NULL)
usage(1);
if ((user_closefrom = atoi(NewArgv[1])) < 3) {
if ((user_closefrom = atoi(optarg)) < 3) {
warningx("the argument to -C must be at least 3");
usage(1);
}
NewArgc--;
NewArgv++;
break;
case 'b':
SET(rval, MODE_BACKGROUND);
@@ -943,10 +915,10 @@ parse_args(argc, argv)
break;
case 'l':
rval = MODE_LIST;
if (excl && excl != 'l')
usage_excl(1);
if (NewArgv[0][2] == 'l' || excl == 'l')
if (excl == 'l')
long_list = 1;
else if (excl)
usage_excl(1);
excl = 'l';
break;
case 'V':
@@ -977,65 +949,42 @@ parse_args(argc, argv)
SET(tgetpass_flags, TGP_STDIN);
break;
case 'U':
/* Must have an associated list user. */
if (NewArgv[1] == NULL)
usage(1);
if ((list_pw = sudo_getpwnam(NewArgv[1])) == NULL)
errorx(1, "unknown user: %s", NewArgv[1]);
NewArgc--;
NewArgv++;
if ((list_pw = sudo_getpwnam(optarg)) == NULL)
errorx(1, "unknown user: %s", optarg);
break;
case 'E':
SET(rval, MODE_PRESERVE_ENV);
break;
#ifdef HAVE_SELINUX
case 'r':
/* Must have an associated SELinux role. */
if (NewArgv[1] == NULL)
usage(1);
user_role = NewArgv[1];
NewArgc--;
NewArgv++;
user_role = optarg;
break;
case 't':
/* Must have an associated SELinux type. */
if (NewArgv[1] == NULL)
usage(1);
user_type = NewArgv[1];
NewArgc--;
NewArgv++;
user_type = optarg;
break;
#endif
case '-':
NewArgc--;
NewArgv++;
goto args_done;
case '\0':
warningx("`-' requires an argument");
usage(1);
default:
warningx("illegal option `%s'", NewArgv[0]);
usage(1);
}
} else if (NewArgv[0][0] != '/' && strchr(NewArgv[0], '=') != NULL) {
/* Could be an environment variable. */
struct list_member *ev;
ev = emalloc(sizeof(*ev));
ev->value = NewArgv[0];
ev->next = sudo_user.env_vars;
sudo_user.env_vars = ev;
} else if (!got_end_of_args && is_envar) {
struct list_member *ev;
/* Store environment variable. */
ev = emalloc(sizeof(*ev));
ev->value = argv[optind];
ev->next = sudo_user.env_vars;
sudo_user.env_vars = ev;
/* Crank optind and resume getopt. */
optind++;
} else {
/* Not an arg */
/* Not an option or an environment variable -- we're done. */
break;
}
NewArgc--;
NewArgv++;
}
args_done:
NewArgc = argc - optind;
NewArgv = argv + optind;
if (NewArgc > 0 && rval == MODE_LIST)
rval = MODE_CHECK;