xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_shell.c (revision 0c1b95be)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Shell Escape I/O Backend
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * The MDB parser implements two forms of shell escapes: (1) traditional adb(1)
317c478bd9Sstevel@tonic-gate  * style shell escapes of the form "!command", which simply allows the user to
327c478bd9Sstevel@tonic-gate  * invoke a command (or shell pipeline) as if they had executed sh -c command
337c478bd9Sstevel@tonic-gate  * and then return to the debugger, and (2) shell pipes of the form "dcmds !
347c478bd9Sstevel@tonic-gate  * command", in which the output of one or more MDB dcmds is sent as standard
357c478bd9Sstevel@tonic-gate  * input to a shell command (or shell pipeline).  Form (1) can be handled
367c478bd9Sstevel@tonic-gate  * entirely from the parser by calling mdb_shell_exec (below); it simply
377c478bd9Sstevel@tonic-gate  * forks the shell, executes the desired command, and waits for completion.
387c478bd9Sstevel@tonic-gate  * Form (2) is slightly more complicated: we construct a UNIX pipe, fork
397c478bd9Sstevel@tonic-gate  * the shell, and then built an fdio object out of the write end of the pipe.
407c478bd9Sstevel@tonic-gate  * We then layer a shellio object (implemented below) and iob on top, and
417c478bd9Sstevel@tonic-gate  * set mdb.m_out to point to this new iob.  The shellio is simply a pass-thru
427c478bd9Sstevel@tonic-gate  * to the fdio, except that its io_close routine performs a waitpid for the
437c478bd9Sstevel@tonic-gate  * forked child process.
447c478bd9Sstevel@tonic-gate  */
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <sys/types.h>
477c478bd9Sstevel@tonic-gate #include <sys/wait.h>
487c478bd9Sstevel@tonic-gate #include <unistd.h>
497c478bd9Sstevel@tonic-gate #include <errno.h>
507c478bd9Sstevel@tonic-gate #include <stdlib.h>
517c478bd9Sstevel@tonic-gate #include <fcntl.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include <mdb/mdb_shell.h>
547c478bd9Sstevel@tonic-gate #include <mdb/mdb_lex.h>
557c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
567c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
577c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
587c478bd9Sstevel@tonic-gate #include <mdb/mdb_frame.h>
597c478bd9Sstevel@tonic-gate #include <mdb/mdb_io_impl.h>
607c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate #define	E_BADEXEC	127	/* Exit status for failed exec */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * We must manually walk the open file descriptors and set FD_CLOEXEC, because
667c478bd9Sstevel@tonic-gate  * we need to be able to print an error if execlp() fails.  If mdb.m_err has
677c478bd9Sstevel@tonic-gate  * been altered, then using closefrom() could close our output file descriptor,
687c478bd9Sstevel@tonic-gate  * preventing us from displaying an error message.  Using FD_CLOEXEC ensures
697c478bd9Sstevel@tonic-gate  * that the file descriptors are only closed if execlp() succeeds.
707c478bd9Sstevel@tonic-gate  */
717c478bd9Sstevel@tonic-gate /*ARGSUSED*/
727c478bd9Sstevel@tonic-gate static int
closefd_walk(void * unused,int fd)737c478bd9Sstevel@tonic-gate closefd_walk(void *unused, int fd)
747c478bd9Sstevel@tonic-gate {
757c478bd9Sstevel@tonic-gate 	if (fd > 2)
767c478bd9Sstevel@tonic-gate 		(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
777c478bd9Sstevel@tonic-gate 	return (0);
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate void
mdb_shell_exec(char * cmd)817c478bd9Sstevel@tonic-gate mdb_shell_exec(char *cmd)
827c478bd9Sstevel@tonic-gate {
837c478bd9Sstevel@tonic-gate 	int status;
847c478bd9Sstevel@tonic-gate 	pid_t pid;
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate 	if (access(mdb.m_shell, X_OK) == -1)
877c478bd9Sstevel@tonic-gate 		yyperror("cannot access %s", mdb.m_shell);
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	if ((pid = vfork()) == -1)
907c478bd9Sstevel@tonic-gate 		yyperror("failed to fork");
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	if (pid == 0) {
937c478bd9Sstevel@tonic-gate 		(void) fdwalk(closefd_walk, NULL);
947c478bd9Sstevel@tonic-gate 		(void) execlp(mdb.m_shell, strbasename(mdb.m_shell),
957c478bd9Sstevel@tonic-gate 		    "-c", cmd, NULL);
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 		warn("failed to exec %s", mdb.m_shell);
987c478bd9Sstevel@tonic-gate 		_exit(E_BADEXEC);
997c478bd9Sstevel@tonic-gate 	}
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	do {
1027c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_SHELL, "waiting for PID %d\n", (int)pid);
1037c478bd9Sstevel@tonic-gate 	} while (waitpid(pid, &status, 0) == -1 && errno == EINTR);
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_SHELL, "waitpid %d -> 0x%x\n", (int)pid, status);
1067c478bd9Sstevel@tonic-gate 	strfree(cmd);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate  * This use of the io_unlink entry point is a little strange: we have stacked
1117c478bd9Sstevel@tonic-gate  * the shellio on top of the fdio, but before the shellio's close routine can
1127c478bd9Sstevel@tonic-gate  * wait for the child process, we need to close the UNIX pipe file descriptor
1137c478bd9Sstevel@tonic-gate  * in order to generate an EOF to terminate the child.  Since each io is
1147c478bd9Sstevel@tonic-gate  * unlinked from its iob before being popped by mdb_iob_destroy, we use the
1157c478bd9Sstevel@tonic-gate  * io_unlink entry point to release the underlying fdio (forcing its io_close
1167c478bd9Sstevel@tonic-gate  * routine to be called) and remove it from the iob's i/o stack out of order.
1177c478bd9Sstevel@tonic-gate  */
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1207c478bd9Sstevel@tonic-gate static void
shellio_unlink(mdb_io_t * io,mdb_iob_t * iob)1217c478bd9Sstevel@tonic-gate shellio_unlink(mdb_io_t *io, mdb_iob_t *iob)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	mdb_io_t *fdio = io->io_next;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	ASSERT(iob->iob_iop == io);
1267c478bd9Sstevel@tonic-gate 	ASSERT(fdio != NULL);
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	io->io_next = fdio->io_next;
1297c478bd9Sstevel@tonic-gate 	fdio->io_next = NULL;
1307c478bd9Sstevel@tonic-gate 	mdb_io_rele(fdio);
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate static void
shellio_close(mdb_io_t * io)1347c478bd9Sstevel@tonic-gate shellio_close(mdb_io_t *io)
1357c478bd9Sstevel@tonic-gate {
1367c478bd9Sstevel@tonic-gate 	pid_t pid = (pid_t)(intptr_t)io->io_data;
1377c478bd9Sstevel@tonic-gate 	int status;
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	do {
1407c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_SHELL, "waiting for PID %d\n", (int)pid);
1417c478bd9Sstevel@tonic-gate 	} while (waitpid(pid, &status, 0) == -1 && errno == EINTR);
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_SHELL, "waitpid %d -> 0x%x\n", (int)pid, status);
1447c478bd9Sstevel@tonic-gate }
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate static const mdb_io_ops_t shellio_ops = {
147*0c1b95beSRichard Lowe 	.io_read = no_io_read,
148*0c1b95beSRichard Lowe 	.io_write = no_io_write,
149*0c1b95beSRichard Lowe 	.io_seek = no_io_seek,
150*0c1b95beSRichard Lowe 	.io_ctl = no_io_ctl,
151*0c1b95beSRichard Lowe 	.io_close = shellio_close,
152*0c1b95beSRichard Lowe 	.io_name = no_io_name,
153*0c1b95beSRichard Lowe 	.io_link = no_io_link,
154*0c1b95beSRichard Lowe 	.io_unlink = shellio_unlink,
155*0c1b95beSRichard Lowe 	.io_setattr = no_io_setattr,
156*0c1b95beSRichard Lowe 	.io_suspend = no_io_suspend,
157*0c1b95beSRichard Lowe 	.io_resume = no_io_resume
1587c478bd9Sstevel@tonic-gate };
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate void
mdb_shell_pipe(char * cmd)1617c478bd9Sstevel@tonic-gate mdb_shell_pipe(char *cmd)
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate 	uint_t iflag = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
1647c478bd9Sstevel@tonic-gate 	mdb_iob_t *iob;
1657c478bd9Sstevel@tonic-gate 	mdb_io_t *io;
1667c478bd9Sstevel@tonic-gate 	int pfds[2];
1677c478bd9Sstevel@tonic-gate 	pid_t pid;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	if (access(mdb.m_shell, X_OK) == -1)
1707c478bd9Sstevel@tonic-gate 		yyperror("cannot access %s", mdb.m_shell);
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	if (pipe(pfds) == -1)
1737c478bd9Sstevel@tonic-gate 		yyperror("failed to open pipe");
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	iob = mdb_iob_create(mdb_fdio_create(pfds[1]), MDB_IOB_WRONLY | iflag);
1767c478bd9Sstevel@tonic-gate 	mdb_iob_clrflags(iob, MDB_IOB_AUTOWRAP | MDB_IOB_INDENT);
1777c478bd9Sstevel@tonic-gate 	mdb_iob_resize(iob, BUFSIZ, BUFSIZ);
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 	if ((pid = vfork()) == -1) {
1807c478bd9Sstevel@tonic-gate 		(void) close(pfds[0]);
1817c478bd9Sstevel@tonic-gate 		(void) close(pfds[1]);
1827c478bd9Sstevel@tonic-gate 		mdb_iob_destroy(iob);
1837c478bd9Sstevel@tonic-gate 		yyperror("failed to fork");
1847c478bd9Sstevel@tonic-gate 	}
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	if (pid == 0) {
1877c478bd9Sstevel@tonic-gate 		(void) close(pfds[1]);
1887c478bd9Sstevel@tonic-gate 		(void) close(STDIN_FILENO);
1897c478bd9Sstevel@tonic-gate 		(void) dup2(pfds[0], STDIN_FILENO);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 		(void) fdwalk(closefd_walk, NULL);
1927c478bd9Sstevel@tonic-gate 		(void) execlp(mdb.m_shell, strbasename(mdb.m_shell),
1937c478bd9Sstevel@tonic-gate 		    "-c", cmd, NULL);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 		warn("failed to exec %s", mdb.m_shell);
1967c478bd9Sstevel@tonic-gate 		_exit(E_BADEXEC);
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	(void) close(pfds[0]);
2007c478bd9Sstevel@tonic-gate 	strfree(cmd);
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	io->io_ops = &shellio_ops;
2057c478bd9Sstevel@tonic-gate 	io->io_data = (void *)(intptr_t)pid;
2067c478bd9Sstevel@tonic-gate 	io->io_next = NULL;
2077c478bd9Sstevel@tonic-gate 	io->io_refcnt = 0;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	mdb_iob_stack_push(&mdb.m_frame->f_ostk, mdb.m_out, yylineno);
2107c478bd9Sstevel@tonic-gate 	mdb_iob_push_io(iob, io);
2117c478bd9Sstevel@tonic-gate 	mdb.m_out = iob;
2127c478bd9Sstevel@tonic-gate }
213