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 { "logname", b_logname }, 100 { "md5sum", b_md5sum }, 101 { "mkdir", b_mkdir }, 102 { "mkfifo", b_mkfifo }, 103 { "mktemp", b_mktemp }, 104 { "mv", b_mv }, 105 { "paste", b_paste }, 106 { "pathchk", b_pathchk }, 107 { "pids", b_pids }, 108 #ifdef ENABLE_PERFORMANCE_PARADOXON 109 { "rev", b_rev }, 110 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 111 { "rm", b_rm }, 112 { "rmdir", b_rmdir }, 113 { "stty", b_stty }, 114 #ifdef ENABLE_PERFORMANCE_PARADOXON 115 { "sum", b_sum }, 116 #endif /* ENABLE_PERFORMANCE_PARADOXON */ 117 { "sync", b_sync }, 118 { "tail", b_tail }, 119 { "tee", b_tee }, 120 { "tty", b_tty }, 121 { "uname", b_uname }, 122 { "uniq", b_uniq }, 123 { "vmstate", b_vmstate }, 124 { "wc", b_wc }, 125 { NULL, NULL } 126 }; 127 128 static inline const bfastpathrec * 129 find_bfastpathrec(const char *name) 130 { 131 unsigned int i; 132 signed int cmpres; 133 for (i = 0; fastpath_builtins[i].name != NULL; i++) { 134 cmpres = strcmp(fastpath_builtins[i].name, name); 135 if (cmpres == 0) 136 return (&fastpath_builtins[i]); 137 else if (cmpres > 0) 138 return (NULL); 139 } 140 return (NULL); 141 } 142 143 static inline 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, originally 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 "\"${cmd}\" \"$@\"\n" 176 "fi\n" 177 "exitval=$?"; 178 179 static inline int 180 script_builtin_main(int argc, char **argv) 181 { 182 int i; 183 Shell_t *shp; 184 Namval_t *np; 185 int exitval; 186 char **xargv; 187 188 /* 189 * Create copy of |argv| array shifted by one position to 190 * emulate $ /usr/bin/sh <scriptname> <args1> <arg2> ... #. 191 * First position is set to "/usr/bin/sh" since other 192 * values may trigger special shell modes (e.g. *rsh* will 193 * trigger "restricted" shell mode etc.). 194 */ 195 xargv = calloc(argc + 2, sizeof (char *)); 196 if (xargv == NULL) 197 return (1); 198 xargv[0] = "/usr/bin/sh"; 199 for (i = 0; i < argc; i++) 200 xargv[i + 1] = argv[i]; 201 xargv[i + 1] = NULL; 202 203 shp = sh_init(argc + 1, xargv, 0); 204 if (!shp) 205 error(ERROR_exit(1), "shell initialisation failed."); 206 if (setjmp(*shp->jmplist) == 0) 207 (void) sh_trap(script, 0); 208 209 np = nv_open("exitval", shp->var_tree, 0); 210 if (!np) 211 error(ERROR_exit(1), "variable %s not found.", "exitval"); 212 exitval = (int)nv_getnum(np); 213 nv_close(np); 214 215 free(xargv); 216 217 return (exitval); 218 } 219 220 int 221 main(int argc, char **argv) 222 { 223 const char *progname; 224 const bfastpathrec *brec; 225 char execnamebuff[PATH_MAX + 1]; 226 227 /* Get program name */ 228 if (pathprog(argv[0], execnamebuff, sizeof (execnamebuff)) <= 0) 229 error(ERROR_exit(1), "could not determinate exec name."); 230 231 progname = (const char *)strrchr(execnamebuff, '/'); 232 233 if (progname != NULL) 234 progname++; 235 else 236 progname = execnamebuff; 237 238 /* Execute command... */ 239 if (brec = find_bfastpathrec(progname)) { 240 /* ... either via a fast path (calling the code directly) ... */ 241 return (fastpath_builtin_main(brec, argc, argv)); 242 } else { 243 /* ... or from within a full shell. */ 244 return (script_builtin_main(argc, argv)); 245 } 246 } 247