517 lines
11 KiB
C
517 lines
11 KiB
C
# ifdef lint
|
|
static char sccsid[] = "@(#)on.c 1.1 94/10/31 Copyr 1985 Sun Micro";
|
|
# endif lint
|
|
|
|
/*
|
|
* on - user interface program for remote execution service
|
|
*
|
|
* Copyright (c) 1985 Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/fcntl.h>
|
|
#include <rpc/rpc.h>
|
|
#include <rpc/key_prot.h>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
#include "rex.h"
|
|
|
|
# define CommandName "on" /* given as argv[0] */
|
|
# define AltCommandName "dbon"
|
|
|
|
extern int errno;
|
|
|
|
/*
|
|
* Note - the following must be long enough for at least two portmap
|
|
* timeouts on the other side.
|
|
*/
|
|
struct timeval LongTimeout = { 123, 0 };
|
|
|
|
int Debug = 0; /* print extra debugging information */
|
|
int Only2 = 0; /* stdout and stderr are the same */
|
|
int Interactive = 0; /* use a pty on server */
|
|
int NoInput = 0; /* don't read standard input */
|
|
|
|
int InOut; /* socket for stdin/stdout */
|
|
int Err; /* socket for stderr */
|
|
|
|
struct sgttyb OldFlags; /* saved tty flags */
|
|
struct sgttyb NewFlags; /* for stop/continue job control */
|
|
CLIENT *Client; /* RPC client handle */
|
|
struct ttysize WindowSize; /* saved window size */
|
|
|
|
|
|
/*
|
|
* window change handler - propagate to remote server
|
|
*/
|
|
sigwinch()
|
|
{
|
|
struct ttysize size;
|
|
enum clnt_stat clstat;
|
|
|
|
ioctl(0, TIOCGSIZE, &size);
|
|
if (bcmp(&size,&WindowSize,sizeof size)==0) return;
|
|
WindowSize = size;
|
|
if (clstat = clnt_call(Client, REXPROC_WINCH,
|
|
xdr_rex_ttysize, &size, xdr_void, NULL, LongTimeout)) {
|
|
fprintf(stderr, "on (size): ");
|
|
clnt_perrno(clstat);
|
|
fprintf(stderr, "\r\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* signal handler - propagate to remote server
|
|
*/
|
|
sendsig(sig)
|
|
int sig;
|
|
{
|
|
enum clnt_stat clstat;
|
|
|
|
if (clstat = clnt_call(Client, REXPROC_SIGNAL,
|
|
xdr_int, &sig, xdr_void, NULL, LongTimeout)) {
|
|
fprintf(stderr, "on (signal): ");
|
|
clnt_perrno(clstat);
|
|
fprintf(stderr, "\r\n");
|
|
}
|
|
}
|
|
|
|
|
|
cont()
|
|
{
|
|
/*
|
|
* Put tty modes back the way they were and tell the rexd server
|
|
* to send the command a SIGCONT signal.
|
|
*/
|
|
if (Interactive) {
|
|
ioctl(0, TIOCSETN, &NewFlags);
|
|
(void) send(InOut, "", 1, MSG_OOB);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* oob -- called when the command invoked by the rexd server is stopped
|
|
* with a SIGTSTP or SIGSTOP signal.
|
|
*/
|
|
oob()
|
|
{
|
|
int atmark;
|
|
char waste[BUFSIZ], mark;
|
|
|
|
for (;;) {
|
|
if (ioctl(InOut, SIOCATMARK, &atmark) < 0) {
|
|
perror("ioctl");
|
|
break;
|
|
}
|
|
if (atmark)
|
|
break;
|
|
(void) read(InOut, waste, sizeof (waste));
|
|
}
|
|
(void) recv(InOut, &mark, 1, MSG_OOB);
|
|
/*
|
|
* Reset tty modes to something sane and stop myself
|
|
*/
|
|
if (Interactive) {
|
|
ioctl(0, TIOCSETN, &OldFlags);
|
|
printf("\r\n");
|
|
}
|
|
kill(getpid(), SIGSTOP);
|
|
}
|
|
|
|
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char *rhost, **cmdp;
|
|
char curdir[MAXPATHLEN];
|
|
char wdhost[MAXHOSTNAMELEN];
|
|
char fsname[MAXPATHLEN];
|
|
char dirwithin[MAXPATHLEN];
|
|
struct rex_start rst;
|
|
struct rex_result result;
|
|
extern char **environ, *rindex();
|
|
extern char *index();
|
|
enum clnt_stat clstat;
|
|
struct hostent *hp;
|
|
struct sockaddr_in server_addr;
|
|
int sock = RPC_ANYSOCK;
|
|
int selmask, zmask, remmask;
|
|
int nfds, cc;
|
|
char *chi, *cho;
|
|
int trying_authdes;
|
|
char netname[MAXNETNAMELEN+1];
|
|
char hostname[MAXHOSTNAMELEN+1];
|
|
char publickey[HEXKEYBYTES+1];
|
|
int i;
|
|
char *domain;
|
|
static char buf[4096];
|
|
|
|
/*
|
|
* we check the invoked command name to see if it should
|
|
* really be a host name.
|
|
*/
|
|
if ( (rhost = rindex(argv[0],'/')) == NULL) {
|
|
rhost = argv[0];
|
|
}
|
|
else {
|
|
rhost++;
|
|
}
|
|
|
|
while (argc > 1 && argv[1][0] == '-') {
|
|
switch (argv[1][1]) {
|
|
case 'd': Debug = 1;
|
|
break;
|
|
case 'i': Interactive = 1;
|
|
break;
|
|
case 'n': NoInput = 1;
|
|
break;
|
|
default:
|
|
printf("Unknown option %s\n",argv[1]);
|
|
}
|
|
argv++;
|
|
argc--;
|
|
}
|
|
if (strcmp(rhost,CommandName) && strcmp(rhost,AltCommandName)) {
|
|
cmdp = &argv[1];
|
|
Interactive = 1;
|
|
} else {
|
|
if (argc < 2)
|
|
usage();
|
|
rhost = argv[1];
|
|
cmdp = &argv[2];
|
|
}
|
|
|
|
/*
|
|
* Can only have one of these
|
|
*/
|
|
if (Interactive && NoInput)
|
|
usage();
|
|
|
|
if ((hp = gethostbyname(rhost)) == NULL) {
|
|
fprintf(stderr, "on: unknown host %s\n", rhost);
|
|
exit(1);
|
|
}
|
|
bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, hp->h_length);
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = 0; /* use pmapper */
|
|
if (Debug) printf("Got the host named %s (%s)\n",
|
|
rhost,inet_ntoa(server_addr.sin_addr));
|
|
trying_authdes = 1;
|
|
|
|
try_auth_unix:
|
|
sock = RPC_ANYSOCK;
|
|
if ((Client = clnttcp_create(&server_addr, REXPROG, REXVERS, &sock,
|
|
0, 0)) == NULL) {
|
|
fprintf(stderr, "on: cannot connect to server on %s\n",
|
|
rhost);
|
|
exit(1);
|
|
}
|
|
|
|
if (Debug) printf("TCP RPC connection created\n");
|
|
if (trying_authdes){
|
|
yp_get_default_domain(&domain);
|
|
|
|
cho = hostname;
|
|
*cho = 0;
|
|
chi= hp->h_name;
|
|
|
|
for ( i = 0 ; (*chi && (i<MAXHOSTNAMELEN) );i++ ){
|
|
if (isupper(*chi)) *cho=tolower(*chi);
|
|
else *cho = *chi;
|
|
cho++;
|
|
chi++;
|
|
}
|
|
*cho=0;
|
|
|
|
if (domain!= NULL){
|
|
if (! host2netname(netname, hostname, domain)){
|
|
trying_authdes = 0;
|
|
if (Debug) printf("host2netname failed %s\n",hp->h_name);
|
|
}
|
|
else {
|
|
if (getpublickey(netname, publickey) == 0) {
|
|
trying_authdes = 0;
|
|
cho=index(hostname , '.');
|
|
if (cho){
|
|
*cho=0;
|
|
if (! host2netname(netname, hostname, domain)){
|
|
if (Debug) printf("host2netname failed %s\n",hp->h_name);
|
|
}
|
|
else {
|
|
if (getpublickey(netname, publickey) != 0)
|
|
trying_authdes = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
trying_authdes = 0;
|
|
if (Debug) printf("yp_get_default_domain failed \n");
|
|
}
|
|
}
|
|
if (trying_authdes){
|
|
Client->cl_auth=authdes_create(netname,60*60, &server_addr, NULL);
|
|
if (Client->cl_auth == NULL){
|
|
if (Debug) printf("authdes_create failed %s\n",netname);
|
|
trying_authdes = 0;
|
|
}
|
|
}
|
|
|
|
|
|
if (trying_authdes == 0) Client->cl_auth = authunix_create_default();
|
|
|
|
|
|
/*
|
|
* Now that we have created the TCP connection, we do some
|
|
* work while the server daemon is being swapped in.
|
|
*/
|
|
if (getwd(curdir) == 0) {
|
|
fprintf(stderr, "on: can't find . (%s)\n",curdir);
|
|
exit(1);
|
|
}
|
|
if (findmount(curdir,wdhost,fsname,dirwithin) == 0) {
|
|
fprintf(stderr, "on: can't locate mount point for %s (%s)\n",
|
|
curdir, dirwithin);
|
|
exit(1);
|
|
}
|
|
if (Debug) printf("wd host %s, fs %s, dir within %s\n",
|
|
wdhost, fsname, dirwithin);
|
|
|
|
Only2 = samefd(1,2);
|
|
rst.rst_cmd = cmdp;
|
|
rst.rst_env = environ;
|
|
rst.rst_host = wdhost;
|
|
rst.rst_fsname = fsname;
|
|
rst.rst_dirwithin = dirwithin;
|
|
rst.rst_port0 = makeport(&InOut);
|
|
rst.rst_port1 = rst.rst_port0; /* same port for stdin */
|
|
rst.rst_flags = 0;
|
|
if (Interactive) {
|
|
rst.rst_flags |= REX_INTERACTIVE;
|
|
gtty(0, &OldFlags);
|
|
gtty(0, &NewFlags);
|
|
NewFlags.sg_flags |= RAW;
|
|
NewFlags.sg_flags &= ~ECHO;
|
|
stty(0, &NewFlags);
|
|
}
|
|
if (Only2) {
|
|
rst.rst_port2 = rst.rst_port1;
|
|
} else {
|
|
rst.rst_port2 = makeport(&Err);
|
|
}
|
|
if (clstat = clnt_call(Client, REXPROC_START,
|
|
xdr_rex_start, &rst, xdr_rex_result, &result, LongTimeout)) {
|
|
if (trying_authdes){
|
|
auth_destroy(Client->cl_auth);
|
|
CLNT_DESTROY(Client);
|
|
trying_authdes=0;
|
|
goto try_auth_unix;
|
|
}
|
|
else {
|
|
fprintf(stderr, "on %s: ", rhost);
|
|
clnt_perrno(clstat);
|
|
fprintf(stderr, "\n");
|
|
Die(1);
|
|
}
|
|
}
|
|
if (result.rlt_stat != 0) {
|
|
fprintf(stderr, "on %s: %s\r",rhost,result.rlt_message);
|
|
Die(1);
|
|
}
|
|
if (Debug) printf("Client call was made\r\n");
|
|
if (Interactive) {
|
|
/*
|
|
* Pass the tty modes along to the server
|
|
*/
|
|
struct rex_ttymode mode;
|
|
int err;
|
|
|
|
mode.basic = OldFlags;
|
|
err = ( ioctl(0, TIOCGETC, &mode.more)<0 ||
|
|
ioctl(0, TIOCGLTC, &mode.yetmore)<0 ||
|
|
ioctl(0, TIOCLGET, &mode.andmore)<0 );
|
|
if (!err && (clstat = clnt_call(Client, REXPROC_MODES,
|
|
xdr_rex_ttymode, &mode, xdr_void, NULL, LongTimeout))) {
|
|
fprintf(stderr, "on (modes) %s: ", rhost);
|
|
clnt_perrno(clstat);
|
|
fprintf(stderr, "\r\n");
|
|
}
|
|
err = ioctl(0, TIOCGSIZE, &WindowSize) < 0;
|
|
if (!err && (clstat = clnt_call(Client, REXPROC_WINCH,
|
|
xdr_rex_ttysize, &WindowSize, xdr_void, NULL, LongTimeout))) {
|
|
fprintf(stderr, "on (size) %s: ", rhost);
|
|
clnt_perrno(clstat);
|
|
fprintf(stderr, "\r\n");
|
|
}
|
|
signal(SIGWINCH, sigwinch);
|
|
signal(SIGINT, sendsig);
|
|
signal(SIGQUIT, sendsig);
|
|
signal(SIGTERM, sendsig);
|
|
}
|
|
signal(SIGCONT, cont);
|
|
signal(SIGURG, oob);
|
|
doaccept(&InOut);
|
|
(void) fcntl(InOut, F_SETOWN, getpid());
|
|
remmask = (1 << InOut);
|
|
if (Debug) printf("accept on stdout\r\n");
|
|
if (!Only2) {
|
|
doaccept(&Err);
|
|
shutdown(Err, 1);
|
|
if (Debug) printf("accept on stderr\r\n");
|
|
remmask |= (1 << Err);
|
|
}
|
|
if (NoInput) {
|
|
/*
|
|
* no input - simulate end-of-file instead
|
|
*/
|
|
zmask = 0;
|
|
shutdown(InOut, 1);
|
|
}
|
|
else {
|
|
/*
|
|
* set up to read standard input, send to remote
|
|
*/
|
|
zmask = 1;
|
|
}
|
|
while (remmask) {
|
|
selmask = remmask | zmask;
|
|
nfds = select(32, &selmask, 0, 0, 0);
|
|
if (nfds <= 0) {
|
|
if (errno == EINTR) continue;
|
|
perror("on: select");
|
|
Die(1);
|
|
}
|
|
if (selmask & (1<<InOut)) {
|
|
cc = read(InOut, buf, sizeof buf);
|
|
if (cc > 0)
|
|
write(1, buf, cc);
|
|
else
|
|
remmask &= ~(1<<InOut);
|
|
}
|
|
if (!Only2 && selmask & (1<<Err)) {
|
|
cc = read(Err, buf, sizeof buf);
|
|
if (cc > 0)
|
|
write(2, buf, cc);
|
|
else
|
|
remmask &= ~(1<<Err);
|
|
}
|
|
if (!NoInput && selmask & (1<<0)) {
|
|
cc = read(0, buf, sizeof buf);
|
|
if (cc > 0)
|
|
write(InOut, buf, cc);
|
|
else {
|
|
/*
|
|
* End of standard input - shutdown outgoing
|
|
* direction of the TCP connection.
|
|
*/
|
|
if (Debug)
|
|
printf("Got EOF - shutting down connection\n");
|
|
zmask = 0;
|
|
shutdown(InOut, 1);
|
|
}
|
|
}
|
|
}
|
|
close(InOut);
|
|
if (!Only2)
|
|
close(Err);
|
|
if (clstat = clnt_call(Client, REXPROC_WAIT,
|
|
xdr_void, 0, xdr_rex_result, &result, LongTimeout)) {
|
|
fprintf(stderr, "on: ");
|
|
clnt_perrno(clstat);
|
|
fprintf(stderr, "\r\n");
|
|
Die(1);
|
|
}
|
|
Die(result.rlt_stat);
|
|
}
|
|
|
|
/*
|
|
* like exit, but resets the terminal state first
|
|
*/
|
|
Die(stat)
|
|
{
|
|
if (Interactive) {
|
|
ioctl(0, TIOCSETN, &OldFlags);
|
|
printf("\r\n");
|
|
}
|
|
exit(stat);
|
|
}
|
|
|
|
|
|
remstop()
|
|
{
|
|
Die(23);
|
|
}
|
|
|
|
/*
|
|
* returns true if we can safely say that the two file descriptors
|
|
* are the "same" (both are same file).
|
|
*/
|
|
samefd(a,b)
|
|
{
|
|
struct stat astat, bstat;
|
|
if (fstat(a,&astat) || fstat(b,&bstat)) return(0);
|
|
if (astat.st_ino == 0 || bstat.st_ino == 0) return(0);
|
|
return( !bcmp( &astat, &bstat, sizeof(astat)) );
|
|
}
|
|
|
|
|
|
/*
|
|
* accept the incoming connection on the given
|
|
* file descriptor, and return the new file descritpor
|
|
*/
|
|
doaccept(fdp)
|
|
int *fdp;
|
|
{
|
|
int fd;
|
|
|
|
fd = accept(*fdp, 0, 0);
|
|
if (fd < 0) {
|
|
perror("accept");
|
|
remstop();
|
|
}
|
|
close(*fdp);
|
|
*fdp = fd;
|
|
}
|
|
|
|
/*
|
|
* create a socket, and return its the port number.
|
|
*/
|
|
makeport(fdp)
|
|
int *fdp;
|
|
{
|
|
struct sockaddr_in sin;
|
|
int fd, len;
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
perror("socket");
|
|
exit(1);
|
|
}
|
|
bzero((char *)&sin, sizeof sin);
|
|
sin.sin_family = AF_INET;
|
|
bind(fd, &sin, sizeof sin);
|
|
len = sizeof sin;
|
|
getsockname(fd, &sin, &len);
|
|
listen(fd, 1);
|
|
*fdp = fd;
|
|
return (htons(sin.sin_port));
|
|
}
|
|
|
|
usage()
|
|
{
|
|
fprintf(stderr, "Usage: on [-i|-n] [-d] machine cmd [args]...\n");
|
|
exit(1);
|
|
}
|