/*
- * Copyright (c) 1996, 1998-2005, 2007-2011
+ * Copyright (c) 1996, 1998-2005, 2007-2012
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
#include "redblack.h"
#include "gettext.h"
#include "sudoers_version.h"
+#include "sudo_conf.h"
#include <gram.h>
struct sudoersfile {
};
TQ_DECLARE(sudoersfile)
+sudo_conv_t sudo_conv; /* NULL in non-plugin */
+
/*
* Function prototypes
*/
static char *get_args(char *);
static char *get_editor(char **);
static void get_hostname(void);
-static char whatnow(void);
-static int check_aliases(int, int);
-static int check_syntax(char *, int, int);
-static int edit_sudoers(struct sudoersfile *, char *, char *, int);
-static int install_sudoers(struct sudoersfile *, int);
+static int whatnow(void);
+static int check_aliases(bool, bool);
+static bool check_syntax(char *, bool, bool, bool);
+static bool edit_sudoers(struct sudoersfile *, char *, char *, int);
+static bool install_sudoers(struct sudoersfile *, bool);
static int print_unused(void *, void *);
-static int reparse_sudoers(char *, char *, int, int);
+static void reparse_sudoers(char *, char *, bool, bool);
static int run_command(char *, char **);
static int visudo_printf(int msg_type, const char *fmt, ...);
static void setup_signals(void);
extern struct rbtree *aliases;
extern FILE *yyin;
extern char *sudoers, *errorfile;
-extern int errorlineno, parse_error;
+extern int errorlineno;
+extern bool parse_error;
/* For getopt(3) */
extern char *optarg;
extern int optind;
sudo_printf_t sudo_printf = visudo_printf;
static struct sudoersfile_list sudoerslist;
static struct rbtree *alias_freelist;
+static bool checkonly;
int
main(int argc, char *argv[])
{
struct sudoersfile *sp;
char *args, *editor, *sudoers_path;
- int ch, checkonly, quiet, strict, oldperms;
+ int ch, exitcode = 0;
+ bool quiet, strict, oldperms;
+ debug_decl(main, SUDO_DEBUG_MAIN)
+
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
- extern char *malloc_options;
- malloc_options = "AFGJPR";
+ {
+ extern char *malloc_options;
+ malloc_options = "AFGJPR";
+ }
#endif
#if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
if (argc < 1)
usage(1);
+ /* Read sudo.conf. */
+ sudo_conf_read();
+
/*
* Arg handling.
*/
- checkonly = oldperms = quiet = strict = FALSE;
+ checkonly = oldperms = quiet = strict = false;
sudoers_path = _PATH_SUDOERS;
while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
switch (ch) {
case 'V':
(void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION);
(void) printf(_("%s grammar version %d\n"), getprogname(), SUDOERS_GRAMMAR_VERSION);
- exit(0);
+ goto done;
case 'c':
checkonly++; /* check mode */
break;
case 'f':
sudoers_path = optarg; /* sudoers file path */
- oldperms = TRUE;
+ oldperms = true;
break;
case 'h':
help();
/* Setup defaults data structures. */
init_defaults();
- if (checkonly)
- exit(check_syntax(sudoers_path, quiet, strict));
+ if (checkonly) {
+ exitcode = check_syntax(sudoers_path, quiet, strict, oldperms) ? 0 : 1;
+ goto done;
+ }
/*
* Parse the existing sudoers file(s) in quiet mode to highlight any
* existing errors and to pull in editor and env_editor conf values.
*/
- if ((yyin = open_sudoers(sudoers_path, TRUE, NULL)) == NULL) {
+ if ((yyin = open_sudoers(sudoers_path, true, NULL)) == NULL) {
error(1, "%s", sudoers_path);
}
init_parser(sudoers_path, 0);
/* Check edited files for a parse error and re-edit any that fail. */
reparse_sudoers(editor, args, strict, quiet);
- /* Install the sudoers temp files. */
+ /* Install the sudoers temp files as needed. */
tq_foreach_fwd(&sudoerslist, sp) {
- if (!sp->modified)
- (void) unlink(sp->tpath);
- else
- (void) install_sudoers(sp, oldperms);
+ (void) install_sudoers(sp, oldperms);
}
- exit(0);
+done:
+ sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
+ exit(exitcode);
}
/*
/*
* Edit each sudoers file.
- * Returns TRUE on success, else FALSE.
+ * Returns true on success, else false.
*/
-static int
+static bool
edit_sudoers(struct sudoersfile *sp, char *editor, char *args, int lineno)
{
int tfd; /* sudoers temp file descriptor */
- int modified; /* was the file modified? */
+ bool modified; /* was the file modified? */
int ac; /* argument count */
char **av; /* argument vector for run_command */
char *cp; /* scratch char pointer */
off_t orig_size; /* starting size of sudoers file */
ssize_t nread; /* number of bytes read */
struct stat sb; /* stat buffer */
+ bool rval = false; /* return value */
+ debug_decl(edit_sudoers, SUDO_DEBUG_UTIL)
if (fstat(sp->fd, &sb) == -1)
error(1, _("unable to stat %s"), sp->path);
/* Find the length of the argument vector */
ac = 3 + (lineno > 0);
if (args) {
- int wasblank;
+ bool wasblank;
ac++;
- for (wasblank = FALSE, cp = args; *cp; cp++) {
+ for (wasblank = false, cp = args; *cp; cp++) {
if (isblank((unsigned char) *cp))
- wasblank = TRUE;
+ wasblank = true;
else if (wasblank) {
- wasblank = FALSE;
+ wasblank = false;
ac++;
}
}
if (stat(sp->tpath, &sb) < 0) {
warningx(_("unable to stat temporary file (%s), %s unchanged"),
sp->tpath, sp->path);
- return FALSE;
+ goto done;
}
if (sb.st_size == 0 && orig_size != 0) {
warningx(_("zero length temporary file (%s), %s unchanged"),
sp->tpath, sp->path);
- sp->modified = TRUE;
- return FALSE;
+ sp->modified = true;
+ goto done;
}
} else {
warningx(_("editor (%s) failed, %s unchanged"), editor, sp->path);
- return FALSE;
+ goto done;
}
/* Set modified bit if use changed the file. */
- modified = TRUE;
+ modified = true;
mtim_get(&sb, &tv);
if (orig_size == sb.st_size && timevalcmp(&orig_mtim, &tv, ==)) {
/*
*/
timevalsub(&tv1, &tv2);
if (timevalisset(&tv2))
- modified = FALSE;
+ modified = false;
}
/*
else
warningx(_("%s unchanged"), sp->tpath);
- return TRUE;
+ rval = true;
+done:
+ debug_return_bool(rval);
}
/*
* Parse sudoers after editing and re-edit any ones that caused a parse error.
- * Returns TRUE on success, else FALSE.
*/
-static int
-reparse_sudoers(char *editor, char *args, int strict, int quiet)
+static void
+reparse_sudoers(char *editor, char *args, bool strict, bool quiet)
{
struct sudoersfile *sp, *last;
FILE *fp;
int ch;
+ debug_decl(reparse_sudoers, SUDO_DEBUG_UTIL)
/*
* Parse the edited sudoers files and do sanity checking
if (yyparse() && !parse_error) {
warningx(_("unabled to parse temporary file (%s), unknown error"),
sp->tpath);
- parse_error = TRUE;
+ parse_error = true;
errorfile = sp->path;
}
fclose(yyin);
if (!parse_error) {
if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER) ||
check_aliases(strict, quiet) != 0) {
- parse_error = TRUE;
+ parse_error = true;
errorfile = sp->path;
}
}
*/
if (parse_error) {
switch (whatnow()) {
- case 'Q' : parse_error = FALSE; /* ignore parse error */
+ case 'Q' : parse_error = false; /* ignore parse error */
break;
- case 'x' : cleanup(0);
+ case 'x' : /* XXX - should return instead of exiting */
+ cleanup(0);
+ sudo_debug_exit_int(__func__, __FILE__,
+ __LINE__, sudo_debug_subsys, 0);
exit(0);
break;
}
}
} while (parse_error);
- return TRUE;
+ debug_return;
}
/*
* Set the owner and mode on a sudoers temp file and
- * move it into place. Returns TRUE on success, else FALSE.
+ * move it into place. Returns true on success, else false.
*/
-static int
-install_sudoers(struct sudoersfile *sp, int oldperms)
+static bool
+install_sudoers(struct sudoersfile *sp, bool oldperms)
{
struct stat sb;
+ bool rval = false;
+ debug_decl(install_sudoers, SUDO_DEBUG_UTIL)
+
+ if (!sp->modified) {
+ /*
+ * No changes but fix owner/mode if needed.
+ */
+ (void) unlink(sp->tpath);
+ if (!oldperms && fstat(sp->fd, &sb) != -1) {
+ if (sb.st_uid != SUDOERS_UID || sb.st_gid != SUDOERS_GID)
+ (void) chown(sp->path, SUDOERS_UID, SUDOERS_GID);
+ if ((sb.st_mode & 0777) != SUDOERS_MODE)
+ (void) chmod(sp->path, SUDOERS_MODE);
+ }
+ rval = true;
+ goto done;
+ }
/*
* Change mode and ownership of temp file so when
if (chown(sp->tpath, SUDOERS_UID, SUDOERS_GID) != 0) {
warning(_("unable to set (uid, gid) of %s to (%u, %u)"),
sp->tpath, SUDOERS_UID, SUDOERS_GID);
- return FALSE;
+ goto done;
}
if (chmod(sp->tpath, SUDOERS_MODE) != 0) {
warning(_("unable to change mode of %s to 0%o"), sp->tpath,
SUDOERS_MODE);
- return FALSE;
+ goto done;
}
}
(void) unlink(sp->tpath);
efree(sp->tpath);
sp->tpath = NULL;
- return FALSE;
+ goto done;
}
efree(sp->tpath);
sp->tpath = NULL;
} else {
warning(_("error renaming %s, %s unchanged"), sp->tpath, sp->path);
(void) unlink(sp->tpath);
- return FALSE;
+ goto done;
}
}
- return TRUE;
+ rval = true;
+done:
+ debug_return_bool(rval);
}
/* STUB */
}
/* STUB */
-int
+bool
user_is_exempt(void)
{
- return FALSE;
+ return false;
}
/* STUB */
int
group_plugin_query(const char *user, const char *group, const struct passwd *pw)
{
- return FALSE;
+ return false;
}
/*
* Assuming a parse error occurred, prompt the user for what they want
* to do now. Returns the first letter of their choice.
*/
-static char
+static int
whatnow(void)
{
int choice, c;
+ debug_decl(whatnow, SUDO_DEBUG_UTIL)
for (;;) {
(void) fputs(_("What now? "), stdout);
case 'e':
case 'x':
case 'Q':
- return choice;
+ debug_return_int(choice);
default:
(void) puts(_("Options are:\n"
" (e)dit sudoers file again\n"
static void
setup_signals(void)
{
- sigaction_t sa;
+ sigaction_t sa;
+ debug_decl(setup_signals, SUDO_DEBUG_UTIL)
- /*
- * Setup signal handlers to cleanup nicely.
- */
- zero_bytes(&sa, sizeof(sa));
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = quit;
- (void) sigaction(SIGTERM, &sa, NULL);
- (void) sigaction(SIGHUP, &sa, NULL);
- (void) sigaction(SIGINT, &sa, NULL);
- (void) sigaction(SIGQUIT, &sa, NULL);
+ /*
+ * Setup signal handlers to cleanup nicely.
+ */
+ zero_bytes(&sa, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = quit;
+ (void) sigaction(SIGTERM, &sa, NULL);
+ (void) sigaction(SIGHUP, &sa, NULL);
+ (void) sigaction(SIGINT, &sa, NULL);
+ (void) sigaction(SIGQUIT, &sa, NULL);
+
+ debug_return;
}
static int
{
int status;
pid_t pid, rv;
+ debug_decl(run_command, SUDO_DEBUG_UTIL)
switch (pid = fork()) {
case -1:
rv = waitpid(pid, &status, 0);
} while (rv == -1 && errno == EINTR);
- if (rv == -1 || !WIFEXITED(status))
- return -1;
- return WEXITSTATUS(status);
+ if (rv != -1)
+ rv = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ debug_return_int(rv);
}
-static int
-check_syntax(char *sudoers_path, int quiet, int strict)
+static bool
+check_owner(const char *path, bool quiet)
{
struct stat sb;
- int error;
+ bool ok = true;
+ debug_decl(check_owner, SUDO_DEBUG_UTIL)
+
+ if (stat(path, &sb) == 0) {
+ if (sb.st_uid != SUDOERS_UID || sb.st_gid != SUDOERS_GID) {
+ ok = false;
+ if (!quiet) {
+ fprintf(stderr,
+ _("%s: wrong owner (uid, gid) should be (%u, %u)\n"),
+ path, SUDOERS_UID, SUDOERS_GID);
+ }
+ }
+ if ((sb.st_mode & 07777) != SUDOERS_MODE) {
+ ok = false;
+ if (!quiet) {
+ fprintf(stderr, _("%s: bad permissions, should be mode 0%o\n"),
+ path, SUDOERS_MODE);
+ }
+ }
+ }
+ debug_return_bool(ok);
+}
+
+static bool
+check_syntax(char *sudoers_path, bool quiet, bool strict, bool oldperms)
+{
+ bool ok = false;
+ debug_decl(check_syntax, SUDO_DEBUG_UTIL)
if (strcmp(sudoers_path, "-") == 0) {
yyin = stdin;
} else if ((yyin = fopen(sudoers_path, "r")) == NULL) {
if (!quiet)
warning(_("unable to open %s"), sudoers_path);
- exit(1);
+ goto done;
}
init_parser(sudoers_path, quiet);
if (yyparse() && !parse_error) {
if (!quiet)
warningx(_("failed to parse %s file, unknown error"), sudoers_path);
- parse_error = TRUE;
+ parse_error = true;
errorfile = sudoers_path;
}
if (!parse_error && check_aliases(strict, quiet) != 0) {
- parse_error = TRUE;
+ parse_error = true;
errorfile = sudoers_path;
}
- error = parse_error;
- if (!quiet) {
- if (parse_error) {
+ ok = !parse_error;
+
+ if (parse_error) {
+ if (!quiet) {
if (errorlineno != -1)
(void) printf(_("parse error in %s near line %d\n"),
errorfile, errorlineno);
else
(void) printf(_("parse error in %s\n"), errorfile);
- } else {
- (void) printf(_("%s: parsed OK\n"), sudoers_path);
- }
- }
- /* Check mode and owner in strict mode. */
- if (strict && yyin != stdin && fstat(fileno(yyin), &sb) == 0) {
- if (sb.st_uid != SUDOERS_UID || sb.st_gid != SUDOERS_GID) {
- error = TRUE;
- if (!quiet) {
- fprintf(stderr,
- _("%s: wrong owner (uid, gid) should be (%u, %u)\n"),
- sudoers_path, SUDOERS_UID, SUDOERS_GID);
- }
}
- if ((sb.st_mode & 07777) != SUDOERS_MODE) {
- error = TRUE;
- if (!quiet) {
- fprintf(stderr, _("%s: bad permissions, should be mode 0%o\n"),
- sudoers_path, SUDOERS_MODE);
- }
+ } else {
+ struct sudoersfile *sp;
+
+ /* Parsed OK, check mode and owner. */
+ if (oldperms || check_owner(sudoers_path, quiet))
+ (void) printf(_("%s: parsed OK\n"), sudoers_path);
+ else
+ ok = false;
+ tq_foreach_fwd(&sudoerslist, sp) {
+ if (oldperms || check_owner(sp->path, quiet))
+ (void) printf(_("%s: parsed OK\n"), sp->path);
+ else
+ ok = false;
}
}
- return error;
+done:
+ debug_return_bool(ok);
}
/*
* any subsequent files #included via a callback from the parser.
*/
FILE *
-open_sudoers(const char *path, int doedit, int *keepopen)
+open_sudoers(const char *path, bool doedit, bool *keepopen)
{
struct sudoersfile *entry;
FILE *fp;
+ int open_flags;
+ debug_decl(open_sudoers, SUDO_DEBUG_UTIL)
+
+ if (checkonly)
+ open_flags = O_RDONLY;
+ else
+ open_flags = O_RDWR | O_CREAT;
/* Check for existing entry */
tq_foreach_fwd(&sudoerslist, entry) {
entry->modified = 0;
entry->prev = entry;
entry->next = NULL;
- entry->fd = open(entry->path, O_RDWR | O_CREAT, SUDOERS_MODE);
+ entry->fd = open(entry->path, open_flags, SUDOERS_MODE);
entry->tpath = NULL;
entry->doedit = doedit;
if (entry->fd == -1) {
warning("%s", entry->path);
efree(entry);
- return NULL;
+ debug_return_ptr(NULL);
}
- if (!lock_file(entry->fd, SUDO_TLOCK))
+ if (!checkonly && !lock_file(entry->fd, SUDO_TLOCK))
errorx(1, _("%s busy, try again later"), entry->path);
if ((fp = fdopen(entry->fd, "r")) == NULL)
error(1, "%s", entry->path);
}
}
if (keepopen != NULL)
- *keepopen = TRUE;
- return fp;
+ *keepopen = true;
+ debug_return_ptr(fp);
}
static char *
get_editor(char **args)
{
char *Editor, *EditorArgs, *EditorPath, *UserEditor, *UserEditorArgs;
+ debug_decl(get_editor, SUDO_DEBUG_UTIL)
/*
* Check VISUAL and EDITOR environment variables to see which editor
errorx(1, _("no editor found (editor path = %s)"), def_editor);
}
*args = EditorArgs;
- return Editor;
+ debug_return_str(Editor);
}
/*
get_args(char *cmnd)
{
char *args;
+ debug_decl(get_args, SUDO_DEBUG_UTIL)
args = cmnd;
while (*args && !isblank((unsigned char) *args))
while (*args && isblank((unsigned char) *args))
args++;
}
- return *args ? args : NULL;
+ debug_return_str(*args ? args : NULL);
}
/*
get_hostname(void)
{
char *p, thost[MAXHOSTNAMELEN + 1];
+ debug_decl(get_hostname, SUDO_DEBUG_UTIL)
- if (gethostname(thost, sizeof(thost)) != 0) {
- user_host = user_shost = "localhost";
- return;
- }
- thost[sizeof(thost) - 1] = '\0';
- user_host = estrdup(thost);
+ if (gethostname(thost, sizeof(thost)) != -1) {
+ thost[sizeof(thost) - 1] = '\0';
+ user_host = estrdup(thost);
- if ((p = strchr(user_host, '.'))) {
- *p = '\0';
- user_shost = estrdup(user_host);
- *p = '.';
+ if ((p = strchr(user_host, '.'))) {
+ *p = '\0';
+ user_shost = estrdup(user_host);
+ *p = '.';
+ } else {
+ user_shost = user_host;
+ }
} else {
- user_shost = user_host;
+ user_host = user_shost = "localhost";
}
+ debug_return;
}
-static int
-alias_remove_recursive(char *name, int type, int strict, int quiet)
+static bool
+alias_remove_recursive(char *name, int type)
{
struct member *m;
struct alias *a;
- int error = 0;
+ bool rval = true;
+ debug_decl(alias_remove_recursive, SUDO_DEBUG_ALIAS)
if ((a = alias_find(name, type)) != NULL) {
tq_foreach_fwd(&a->members, m) {
if (m->type == ALIAS) {
- if (!alias_remove_recursive(m->name, type, strict, quiet))
- error = 1;
+ if (!alias_remove_recursive(m->name, type))
+ rval = false;
}
}
}
a = alias_remove(name, type);
if (a)
rbinsert(alias_freelist, a);
- return error;
+ debug_return_bool(rval);
}
static int
{
struct member *m;
struct alias *a;
- int error = 0;
+ int errors = 0;
+ debug_decl(check_alias, SUDO_DEBUG_ALIAS)
if ((a = alias_find(name, type)) != NULL) {
/* check alias contents */
tq_foreach_fwd(&a->members, m) {
if (m->type == ALIAS)
- error += check_alias(m->name, type, strict, quiet);
+ errors += check_alias(m->name, type, strict, quiet);
}
} else {
if (!quiet) {
type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :
"Unknown", name);
}
- error++;
+ errors++;
}
- return error;
+ debug_return_int(errors);
}
/*
* aliases or unused aliases.
*/
static int
-check_aliases(int strict, int quiet)
+check_aliases(bool strict, bool quiet)
{
struct cmndspec *cs;
struct member *m, *binding;
struct privilege *priv;
struct userspec *us;
struct defaults *d;
- int atype, error = 0;
+ int atype, errors = 0;
+ debug_decl(check_aliases, SUDO_DEBUG_ALIAS)
alias_freelist = rbcreate(alias_compare);
tq_foreach_fwd(&us->users, m) {
if (m->type == ALIAS) {
alias_seqno++;
- error += check_alias(m->name, USERALIAS, strict, quiet);
+ errors += check_alias(m->name, USERALIAS, strict, quiet);
}
}
tq_foreach_fwd(&us->privileges, priv) {
tq_foreach_fwd(&priv->hostlist, m) {
if (m->type == ALIAS) {
alias_seqno++;
- error += check_alias(m->name, HOSTALIAS, strict, quiet);
+ errors += check_alias(m->name, HOSTALIAS, strict, quiet);
}
}
tq_foreach_fwd(&priv->cmndlist, cs) {
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m->type == ALIAS) {
alias_seqno++;
- error += check_alias(m->name, RUNASALIAS, strict, quiet);
+ errors += check_alias(m->name, RUNASALIAS, strict, quiet);
}
}
if ((m = cs->cmnd)->type == ALIAS) {
alias_seqno++;
- error += check_alias(m->name, CMNDALIAS, strict, quiet);
+ errors += check_alias(m->name, CMNDALIAS, strict, quiet);
}
}
}
tq_foreach_fwd(&us->users, m) {
if (m->type == ALIAS) {
alias_seqno++;
- if (!alias_remove_recursive(m->name, USERALIAS, strict, quiet))
- error++;
+ if (!alias_remove_recursive(m->name, USERALIAS))
+ errors++;
}
}
tq_foreach_fwd(&us->privileges, priv) {
tq_foreach_fwd(&priv->hostlist, m) {
if (m->type == ALIAS) {
alias_seqno++;
- if (!alias_remove_recursive(m->name, HOSTALIAS, strict,
- quiet))
- error++;
+ if (!alias_remove_recursive(m->name, HOSTALIAS))
+ errors++;
}
}
tq_foreach_fwd(&priv->cmndlist, cs) {
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m->type == ALIAS) {
alias_seqno++;
- if (!alias_remove_recursive(m->name, RUNASALIAS,
- strict, quiet))
- error++;
+ if (!alias_remove_recursive(m->name, RUNASALIAS))
+ errors++;
}
}
if ((m = cs->cmnd)->type == ALIAS) {
alias_seqno++;
- if (!alias_remove_recursive(m->name, CMNDALIAS, strict,
- quiet))
- error++;
+ if (!alias_remove_recursive(m->name, CMNDALIAS))
+ errors++;
}
}
}
for (m = binding; m != NULL; m = m->next) {
if (m->type == ALIAS) {
alias_seqno++;
- if (!alias_remove_recursive(m->name, atype, strict, quiet))
- error++;
+ if (!alias_remove_recursive(m->name, atype))
+ errors++;
}
}
}
if (!no_aliases() && !quiet)
alias_apply(print_unused, strict ? "Error" : "Warning");
- return strict ? error : 0;
+ debug_return_int(strict ? errors : 0);
}
static int
struct alias *a = (struct alias *)v1;
char *prefix = (char *)v2;
- warningx(_("%s: unused %s_Alias %s"), prefix,
+ warningx2(_("%s: unused %s_Alias %s"), prefix,
a->type == HOSTALIAS ? "Host" : a->type == CMNDALIAS ? "Cmnd" :
a->type == USERALIAS ? "User" : a->type == RUNASALIAS ? "Runas" :
"Unknown", a->name);