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 * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. 27 */ 28 29 /* 30 * alias.c is a C version of the alias.sh wrapper (which links ksh 31 * builtins to commands in /usr/bin/, e.g. calling this wrapper as 32 * /usr/bin/alias will call the ksh "alias" builtin, running it as 33 * /usr/bin/cut will call the ksh "cut" builtin etc. 34 */ 35 36 #include <defs.h> 37 #include <shell.h> 38 #include <nval.h> 39 #include <cmdext.h> 40 #include <stdio.h> 41 #include <setjmp.h> 42 43 #undef calloc 44 #undef free 45 46 typedef struct { 47 const char *name; 48 int (* func)(int, char **, Shbltin_t *); 49 } bfastpathrec; 50 51 /* 52 * We've disabled the "fastpath" codepath for some commands below 53 * because it causes a paradoxon for large input files (as used by 54 * ON PerfPIT for testing). For /usr/bin/rev (where the issue was 55 * first discovered) it looks like this: 56 * - for small files like /etc/profile the fastpath is faster in a loop 57 * with 1000 iterations (8 seconds with fastpath, 14 seconds without 58 * fastpath) 59 * - for large files (/usr/pub/UTF-8 replicated until the test file 60 * reaches 24884706 bytes) the benchmark reverses: The fastpath now 61 * needs 40 seconds and without fastpath it needs 30 seconds (for 100 62 * iterations). 63 */ 64 #if 0 65 #define ENABLE_PERFORMANCE_PARADOXON 1 66 #endif 67 68 /* 69 * List of libcmd builtins which do not require a |Shell_t| context. 70 * This list was automatically generated from <ast/cmdext.h> 71 */ 72 static const 73 bfastpathrec fastpath_builtins[] = 74 { 75 /* This list must be alphabetically sorted for |strcmp()| usage */ 76 { "basename", b_basename }, 77 { "cat", b_cat }, 78 { "chgrp", b_chgrp }, 79 { "chmod", b_chmod }, 80 { "chown", b_chown }, 81 #ifdef ENABLE_PERFORMANCE_PARADOXON 82 { "cksum", b_cksum }, 83 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 84 { "cmp", b_cmp }, 85 { "comm", b_comm }, 86 { "cp", b_cp }, 87 { "cut", b_cut }, 88 { "date", b_date }, 89 { "dirname", b_dirname }, 90 { "expr", b_expr }, 91 { "fds", b_fds }, 92 { "fmt", b_fmt }, 93 { "fold", b_fold }, 94 { "getconf", b_getconf }, 95 { "head", b_head }, 96 { "id", b_id }, 97 { "join", b_join }, 98 { "ln", b_ln }, 99 { "md5sum", b_md5sum }, 100 { "mkdir", b_mkdir }, 101 { "mkfifo", b_mkfifo }, 102 { "mktemp", b_mktemp }, 103 { "mv", b_mv }, 104 { "paste", b_paste }, 105 { "pathchk", b_pathchk }, 106 { "pids", b_pids }, 107 #ifdef ENABLE_PERFORMANCE_PARADOXON 108 { "rev", b_rev }, 109 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 110 { "rm", b_rm }, 111 { "rmdir", b_rmdir }, 112 { "stty", b_stty }, 113 #ifdef ENABLE_PERFORMANCE_PARADOXON 114 { "sum", b_sum }, 115 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 116 { "sync", b_sync }, 117 { "tail", b_tail }, 118 { "tee", b_tee }, 119 { "tty", b_tty }, 120 { "uname", b_uname }, 121 { "uniq", b_uniq }, 122 { "vmstate", b_vmstate }, 123 { "wc", b_wc }, 124 { NULL, NULL } 125 }; 126 127 static inline const bfastpathrec * 128 find_bfastpathrec(const char *name) 129 { 130 unsigned int i; 131 signed int cmpres; 132 for (i = 0; fastpath_builtins[i].name != NULL; i++) { 133 cmpres = strcmp(fastpath_builtins[i].name, name); 134 if (cmpres == 0) 135 return (&fastpath_builtins[i]); 136 else if (cmpres > 0) 137 return (NULL); 138 } 139 return (NULL); 140 } 141 142 static inline int 143 fastpath_builtin_main(const bfastpathrec *brec, int argc, char *argv[]) 144 { 145 setlocale(LC_ALL, ""); /* calls |_ast_setlocale()| */ 146 147 return ((*brec->func)(argc, argv, NULL)); 148 } 149 150 151 /* Builtin script, originally derived from alias.sh */ 152 static const char *script = "\n" 153 /* Get name of builtin */ 154 "typeset cmd=\"${0##*/}\"\n" 155 /* 156 * If the requested command is not an alias, load it explicitly 157 * to make sure it is not bound to a path (those built-ins which 158 * are mapped via shell aliases point to commands which are 159 * "special shell built-ins" which cannot be bound to a specific 160 * PATH element) - otherwise we may execute the wrong command 161 * if an executable with the same name sits in a PATH element 162 * before /usr/bin (e.g. /usr/xpg4/bin/ls would be executed 163 * before /usr/bin/ls if the path was something like 164 * PATH=/usr/xpg4/bin:/usr/bin). 165 */ 166 "if [[ \"${cmd}\" != ~(Elr)(alias|unalias|command) ]] && " 167 "! alias \"${cmd}\" >/dev/null 2>&1 ; then\n" 168 "PATH='' builtin \"${cmd}\"\n" 169 "fi\n" 170 /* command is a keyword and needs to be handled separately */ 171 "if [[ \"${cmd}\" == \"command\" ]] ; then\n" 172 "command \"$@\"\n" 173 "else\n" 174 "\"${cmd}\" \"$@\"\n" 175 "fi\n" 176 "exitval=$?"; 177 178 static int 179 script_builtin_main(int argc, char **argv) 180 { 181 int i; 182 Shell_t *shp; 183 Namval_t *np; 184 int exitval; 185 char **xargv; 186 187 /* 188 * Create copy of |argv| array shifted by one position to 189 * emulate $ /usr/bin/sh <scriptname> <args1> <arg2> ... #. 190 * First position is set to "/usr/bin/sh" since other 191 * values may trigger special shell modes (e.g. *rsh* will 192 * trigger "restricted" shell mode etc.). 193 */ 194 xargv = calloc(argc + 2, sizeof (char *)); 195 if (xargv == NULL) 196 return (1); 197 xargv[0] = "/usr/bin/sh"; 198 for (i = 0; i < argc; i++) 199 xargv[i + 1] = argv[i]; 200 xargv[i + 1] = NULL; 201 202 shp = sh_init(argc + 1, xargv, 0); 203 if (!shp) 204 error(ERROR_exit(1), "shell initialisation failed."); 205 if (setjmp(*shp->jmplist) == 0) 206 (void) sh_trap(script, 0); 207 208 np = nv_open("exitval", shp->var_tree, 0); 209 if (!np) 210 error(ERROR_exit(1), "variable %s not found.", "exitval"); 211 exitval = (int)nv_getnum(np); 212 nv_close(np); 213 214 free(xargv); 215 216 return (exitval); 217 } 218 219 int 220 main(int argc, char **argv) 221 { 222 const char *progname; 223 const bfastpathrec *brec; 224 char execnamebuff[PATH_MAX + 1]; 225 226 /* Get program name */ 227 if (pathprog(argv[0], execnamebuff, sizeof (execnamebuff)) <= 0) 228 error(ERROR_exit(1), "could not determinate exec name."); 229 230 progname = (const char *)strrchr(execnamebuff, '/'); 231 232 if (progname != NULL) 233 progname++; 234 else 235 progname = execnamebuff; 236 237 /* Execute command... */ 238 if (brec = find_bfastpathrec(progname)) { 239 /* ... either via a fast path (calling the code directly) ... */ 240 return (fastpath_builtin_main(brec, argc, argv)); 241 } else { 242 /* ... or from within a full shell. */ 243 return (script_builtin_main(argc, argv)); 244 } 245 } 246