xref: /titanic_50/usr/src/cmd/ksh/builtins/alias.c (revision d185aafa0dcc4aa576fc668a1d1ce3a28660c456)
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 *
find_bfastpathrec(const char * name)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
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, 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
script_builtin_main(int argc,char * argv[])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
main(int argc,char * argv[])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