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