1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * alias.c is a C version of the alias.sh wrapper (which links ksh 29 * builtins to commands in /usr/bin/, e.g. calling this wrapper as 30 * /usr/bin/alias will call the ksh "alias" builtin, running it as 31 * /usr/bin/cut will call the ksh "cut" builtin etc. 32 */ 33 34 #include <shell.h> 35 #include <nval.h> 36 #include <cmdext.h> 37 #include <stdio.h> 38 39 typedef struct { 40 const char *name; 41 int (* func)(int, char **, void *); 42 } bfastpathrec; 43 44 /* 45 * We've disabled the "fastpath" codepath for some commands below 46 * because it causes a paradoxon for large input files (as used by 47 * ON PerfPIT for testing). For /usr/bin/rev (where the issue was 48 * first discovered) it looks like this: 49 * - for small files like /etc/profile the fastpath is faster in a loop 50 * with 1000 iterations (8 seconds with fastpath, 14 seconds without 51 * fastpath) 52 * - for large files (/usr/pub/UTF-8 replicated until the test file 53 * reaches 24884706 bytes) the benchmark reverses: The fastpath now 54 * needs 40 seconds and without fastpath it needs 30 seconds (for 100 55 * iterations). 56 */ 57 #if 0 58 #define ENABLE_PERFORMANCE_PARADOXON 1 59 #endif 60 61 /* 62 * List of libcmd builtins which do not require a |Shell_t| context. 63 * This list was automatically generated from <ast/cmdext.h> 64 */ 65 static const 66 bfastpathrec fastpath_builtins[] = 67 { 68 /* This list must be alphabetically sorted for |strcmp()| usage */ 69 { "basename", b_basename }, 70 { "cat", b_cat }, 71 { "chgrp", b_chgrp }, 72 { "chmod", b_chmod }, 73 { "chown", b_chown }, 74 #ifdef ENABLE_PERFORMANCE_PARADOXON 75 { "cksum", b_cksum }, 76 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 77 { "cmp", b_cmp }, 78 { "comm", b_comm }, 79 { "cp", b_cp }, 80 { "cut", b_cut }, 81 { "date", b_date }, 82 { "dirname", b_dirname }, 83 { "egrep", b_egrep }, 84 { "expr", b_expr }, 85 { "fds", b_fds }, 86 { "fgrep", b_fgrep }, 87 { "fmt", b_fmt }, 88 { "fold", b_fold }, 89 { "getconf", b_getconf }, 90 { "grep", b_grep }, 91 { "head", b_head }, 92 { "id", b_id }, 93 { "join", b_join }, 94 { "ln", b_ln }, 95 { "logname", b_logname }, 96 { "md5sum", b_md5sum }, 97 { "mkdir", b_mkdir }, 98 { "mkfifo", b_mkfifo }, 99 { "mktemp", b_mktemp }, 100 { "mv", b_mv }, 101 { "paste", b_paste }, 102 { "pathchk", b_pathchk }, 103 { "pids", b_pids }, 104 { "readlink", b_readlink }, 105 #ifdef ENABLE_PERFORMANCE_PARADOXON 106 { "rev", b_rev }, 107 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 108 { "rm", b_rm }, 109 { "rmdir", b_rmdir }, 110 { "stty", b_stty }, 111 #ifdef ENABLE_PERFORMANCE_PARADOXON 112 { "sum", b_sum }, 113 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 114 { "sync", b_sync }, 115 { "tail", b_tail }, 116 { "tee", b_tee }, 117 { "tty", b_tty }, 118 { "uname", b_uname }, 119 { "uniq", b_uniq }, 120 { "wc", b_wc }, 121 { "xgrep", b_xgrep }, 122 { NULL, (int (*)(int, char **, void *))NULL } 123 }; 124 125 static inline 126 const bfastpathrec * 127 find_bfastpathrec(const char *name) 128 { 129 unsigned int i; 130 signed int cmpres; 131 for (i = 0; fastpath_builtins[i].name != NULL; i++) { 132 cmpres = strcmp(fastpath_builtins[i].name, name); 133 if (cmpres == 0) 134 return (&fastpath_builtins[i]); 135 else if (cmpres > 0) 136 return (NULL); 137 138 } 139 return (NULL); 140 } 141 142 static inline 143 int 144 fastpath_builtin_main(const bfastpathrec *brec, int argc, char *argv[]) 145 { 146 setlocale(LC_ALL, ""); /* calls |_ast_setlocale()| */ 147 148 return ((*brec->func)(argc, argv, NULL)); 149 } 150 151 152 /* Builtin script, original derived from alias.sh */ 153 static const char *script = "\n" 154 /* Get name of builtin */ 155 "typeset cmd=\"${0##*/}\"\n" 156 /* 157 * If the requested command is not an alias load it explicitly 158 * to make sure it is not bound to a path (those built-ins which 159 * are mapped via shell aliases point to commands which are 160 * "special shell built-ins" which cannot be bound to a specific 161 * PATH element) - otherwise we may execute the wrong command 162 * if an executable with the same name sits in a PATH element 163 * before /usr/bin (e.g. /usr/xpg4/bin/ls would be executed 164 * before /usr/bin/ls if the path was something like 165 * PATH=/usr/xpg4/bin:/usr/bin). 166 */ 167 "if [[ \"${cmd}\" != ~(Elr)(alias|unalias|command) ]] && " 168 "! alias \"${cmd}\" >/dev/null 2>&1 ; then\n" 169 "PATH='' builtin \"${cmd}\"\n" 170 "fi\n" 171 /* command is a keyword and needs to be handled separately */ 172 "if [[ \"${cmd}\" == \"command\" ]] ; then\n" 173 "command \"$@\"\n" 174 "else\n" 175 #ifdef WORKAROUND_FOR_ALIAS_CRASH 176 /* 177 * Work around a crash in /usr/bin/alias when invalid options are 178 * passed (e.g. $ /usr/bin/alias -c #). The shell code will call 179 * an error handler which does a |longjmp()| but somehow the code 180 * failed to do the |setjmp()| before this point. 181 * Putting the "alias" command in a subshell avoids the crash. 182 * Real cause of the issue is under investigation and a fix be 183 * delivered with the next ast-ksh update. 184 */ 185 "( \"${cmd}\" \"$@\" )\n" 186 #else 187 "\"${cmd}\" \"$@\"\n" 188 #endif /* WORKAROUND_FOR_ALIAS_CRASH */ 189 "fi\n" 190 "exitval=$?"; 191 192 193 static inline 194 int 195 script_builtin_main(int argc, char *argv[]) 196 { 197 int i; 198 Shell_t *shp; 199 Namval_t *np; 200 int exitval; 201 202 /* 203 * Create copy of |argv| array shifted by one position to 204 * emulate $ /usr/bin/sh <scriptname> <args1> <arg2> ... #. 205 * First position is set to "/usr/bin/sh" since other 206 * values may trigger special shell modes (e.g. *rsh* will 207 * trigger "restricted" shell mode etc.). 208 */ 209 char *xargv[argc+2]; 210 xargv[0] = "/usr/bin/sh"; 211 xargv[1] = "scriptname"; 212 for (i = 0; i < argc; i++) { 213 xargv[i+1] = argv[i]; 214 } 215 xargv[i+1] = NULL; 216 217 shp = sh_init(argc+1, xargv, 0); 218 if (!shp) 219 error(ERROR_exit(1), "shell initialisation failed."); 220 (void) sh_trap(script, 0); 221 222 np = nv_open("exitval", shp->var_tree, 0); 223 if (!np) 224 error(ERROR_exit(1), "variable %s not found.", "exitval"); 225 exitval = (int)nv_getnum(np); 226 nv_close(np); 227 228 return (exitval); 229 } 230 231 int 232 main(int argc, char *argv[]) 233 { 234 const char *progname; 235 const bfastpathrec *brec; 236 char execnamebuff[PATH_MAX+1]; 237 238 /* Get program name */ 239 if (pathprog(argv[0], execnamebuff, sizeof (execnamebuff)) <= 0) 240 error(ERROR_exit(1), "could not determinate exec name."); 241 242 progname = (const char *)strrchr(execnamebuff, '/'); 243 if (progname != NULL) { 244 progname++; 245 } 246 else 247 { 248 progname = execnamebuff; 249 } 250 251 /* Execute command... */ 252 if (brec = find_bfastpathrec(progname)) { 253 /* ... either via a fast path (calling the code directly) ... */ 254 return (fastpath_builtin_main(brec, argc, argv)); 255 } 256 else 257 { 258 /* ... or from within a full shell. */ 259 return (script_builtin_main(argc, argv)); 260 } 261 } 262