/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * alias.c is a C version of the alias.sh wrapper (which links ksh * builtins to commands in /usr/bin/, e.g. calling this wrapper as * /usr/bin/alias will call the ksh "alias" builtin, running it as * /usr/bin/cut will call the ksh "cut" builtin etc. */ #include #include #include #include typedef struct { const char *name; int (* func)(int, char **, void *); } bfastpathrec; /* * We've disabled the "fastpath" codepath for some commands below * because it causes a paradoxon for large input files (as used by * ON PerfPIT for testing). For /usr/bin/rev (where the issue was * first discovered) it looks like this: * - for small files like /etc/profile the fastpath is faster in a loop * with 1000 iterations (8 seconds with fastpath, 14 seconds without * fastpath) * - for large files (/usr/pub/UTF-8 replicated until the test file * reaches 24884706 bytes) the benchmark reverses: The fastpath now * needs 40 seconds and without fastpath it needs 30 seconds (for 100 * iterations). */ #if 0 #define ENABLE_PERFORMANCE_PARADOXON 1 #endif /* * List of libcmd builtins which do not require a |Shell_t| context. * This list was automatically generated from */ static const bfastpathrec fastpath_builtins[] = { /* This list must be alphabetically sorted for |strcmp()| usage */ { "basename", b_basename }, { "cat", b_cat }, { "chgrp", b_chgrp }, { "chmod", b_chmod }, { "chown", b_chown }, #ifdef ENABLE_PERFORMANCE_PARADOXON { "cksum", b_cksum }, #endif /* ENABLE_PERFORMANCE_PARADOXON */ { "cmp", b_cmp }, { "comm", b_comm }, { "cp", b_cp }, { "cut", b_cut }, { "date", b_date }, { "dirname", b_dirname }, { "egrep", b_egrep }, { "expr", b_expr }, { "fds", b_fds }, { "fgrep", b_fgrep }, { "fmt", b_fmt }, { "fold", b_fold }, { "getconf", b_getconf }, { "grep", b_grep }, { "head", b_head }, { "id", b_id }, { "join", b_join }, { "ln", b_ln }, { "logname", b_logname }, { "md5sum", b_md5sum }, { "mkdir", b_mkdir }, { "mkfifo", b_mkfifo }, { "mktemp", b_mktemp }, { "mv", b_mv }, { "paste", b_paste }, { "pathchk", b_pathchk }, { "pids", b_pids }, { "readlink", b_readlink }, #ifdef ENABLE_PERFORMANCE_PARADOXON { "rev", b_rev }, #endif /* ENABLE_PERFORMANCE_PARADOXON */ { "rm", b_rm }, { "rmdir", b_rmdir }, { "stty", b_stty }, #ifdef ENABLE_PERFORMANCE_PARADOXON { "sum", b_sum }, #endif /* ENABLE_PERFORMANCE_PARADOXON */ { "sync", b_sync }, { "tail", b_tail }, { "tee", b_tee }, { "tty", b_tty }, { "uname", b_uname }, { "uniq", b_uniq }, { "wc", b_wc }, { "xgrep", b_xgrep }, { NULL, (int (*)(int, char **, void *))NULL } }; static inline const bfastpathrec * find_bfastpathrec(const char *name) { unsigned int i; signed int cmpres; for (i = 0; fastpath_builtins[i].name != NULL; i++) { cmpres = strcmp(fastpath_builtins[i].name, name); if (cmpres == 0) return (&fastpath_builtins[i]); else if (cmpres > 0) return (NULL); } return (NULL); } static inline int fastpath_builtin_main(const bfastpathrec *brec, int argc, char *argv[]) { setlocale(LC_ALL, ""); /* calls |_ast_setlocale()| */ return ((*brec->func)(argc, argv, NULL)); } /* Builtin script, original derived from alias.sh */ static const char *script = "\n" /* Get name of builtin */ "typeset cmd=\"${0##*/}\"\n" /* * If the requested command is not an alias load it explicitly * to make sure it is not bound to a path (those built-ins which * are mapped via shell aliases point to commands which are * "special shell built-ins" which cannot be bound to a specific * PATH element) - otherwise we may execute the wrong command * if an executable with the same name sits in a PATH element * before /usr/bin (e.g. /usr/xpg4/bin/ls would be executed * before /usr/bin/ls if the path was something like * PATH=/usr/xpg4/bin:/usr/bin). */ "if [[ \"${cmd}\" != ~(Elr)(alias|unalias|command) ]] && " "! alias \"${cmd}\" >/dev/null 2>&1 ; then\n" "PATH='' builtin \"${cmd}\"\n" "fi\n" /* command is a keyword and needs to be handled separately */ "if [[ \"${cmd}\" == \"command\" ]] ; then\n" "command \"$@\"\n" "else\n" #ifdef WORKAROUND_FOR_ALIAS_CRASH /* * Work around a crash in /usr/bin/alias when invalid options are * passed (e.g. $ /usr/bin/alias -c #). The shell code will call * an error handler which does a |longjmp()| but somehow the code * failed to do the |setjmp()| before this point. * Putting the "alias" command in a subshell avoids the crash. * Real cause of the issue is under investigation and a fix be * delivered with the next ast-ksh update. */ "( \"${cmd}\" \"$@\" )\n" #else "\"${cmd}\" \"$@\"\n" #endif /* WORKAROUND_FOR_ALIAS_CRASH */ "fi\n" "exitval=$?"; static inline int script_builtin_main(int argc, char *argv[]) { int i; Shell_t *shp; Namval_t *np; int exitval; /* * Create copy of |argv| array shifted by one position to * emulate $ /usr/bin/sh ... #. * First position is set to "/usr/bin/sh" since other * values may trigger special shell modes (e.g. *rsh* will * trigger "restricted" shell mode etc.). */ char *xargv[argc+2]; xargv[0] = "/usr/bin/sh"; xargv[1] = "scriptname"; for (i = 0; i < argc; i++) { xargv[i+1] = argv[i]; } xargv[i+1] = NULL; shp = sh_init(argc+1, xargv, 0); if (!shp) error(ERROR_exit(1), "shell initialisation failed."); (void) sh_trap(script, 0); np = nv_open("exitval", shp->var_tree, 0); if (!np) error(ERROR_exit(1), "variable %s not found.", "exitval"); exitval = (int)nv_getnum(np); nv_close(np); return (exitval); } int main(int argc, char *argv[]) { const char *progname; const bfastpathrec *brec; char execnamebuff[PATH_MAX+1]; /* Get program name */ if (pathprog(argv[0], execnamebuff, sizeof (execnamebuff)) <= 0) error(ERROR_exit(1), "could not determinate exec name."); progname = (const char *)strrchr(execnamebuff, '/'); if (progname != NULL) { progname++; } else { progname = execnamebuff; } /* Execute command... */ if (brec = find_bfastpathrec(progname)) { /* ... either via a fast path (calling the code directly) ... */ return (fastpath_builtin_main(brec, argc, argv)); } else { /* ... or from within a full shell. */ return (script_builtin_main(argc, argv)); } }