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 *
find_bfastpathrec(const char * name)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
fastpath_builtin_main(const bfastpathrec * brec,int argc,char * argv[])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
script_builtin_main(int argc,char ** argv)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
main(int argc,char ** argv)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