Relay-Version: version B 2.10.3 beta 11/15/85; site seismo.CSS.GOV Posting-Version: version B 2.10.2 9/3/84; site panda.UUCP Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: rsend - BSD communications program Message-ID: <1242@panda.UUCP> Date: 31 Dec 85 14:13:10 GMT Sender: jpn@panda.UUCP Lines: 1162 Approved: jpn@panda.UUCP Mod.sources: Volume 3, Issue 73 Submitted by: seismo!enea!ttds!zap (Svante Lindahl) Here is a program that we use all the time, instead of write and talk. It can be used for one-way communication (like write) and over a local network (like talk). It is very BSD:ish, sockets and all. We have it running on three VAXen and several SUNs on our ethernet, and communication between VAX and SUN works fine (unlike talk :-( (ours does work now!)). Please, no flames, this was my first UNIX-hack, my first C-hack and my first communications program. Accurate criticism is of course welcome. : This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. echo 'Extracting README' sed 's/^X//' > README << '+ END-OF-FILE README' XThis is the README file for rsend. XThe other files you need are: X rsend.h - the headerfile for rsend.c and rsendd.c X rsend.c - the user interface X rsendd.c - the rsend daemon X rsend.1 - the manual entry for rsend X Makefile - makes rsend and rsendd X Xrsend is a program for sending messages to other users on a local machine Xor other machines in a local area network. (cf. SEND on TOPS-10). It is Ximplemented using sockets and will thus only run on 4.xBSD (for x>=2). X XBefore typing make, take a look at the makefile and see which compile- Xtime flags you want to use. X XWhen installing it you need to do the following: X X make an entry for rsend in /etc/services looking something X like this: X rsend 777/udp X (Where 777 is an arbitrary address less than 1024) X X put the following in /etc/rc.local to start the daemon when X the machine boots: X if [ -f /usr/net/rsendd ]; then X /usr/net/rsendd & echo -n ' rsendd' > /dev/console X fi X (or at least something looking remotely like this, depending X on whether you echo the names of local daemons to the console, X and where you prefer to install rsendd) X XNote that the daemon has to be started by root. + END-OF-FILE README chmod 'u=rw,g=rw,o=r' 'README' echo ' -rw-rw-r-- 1 zap 1154 Dec 30 23:04 README (as sent)' echo -n ' ' /bin/ls -l README echo 'Extracting Makefile' sed 's/^X//' > Makefile << '+ END-OF-FILE Makefile' XDAEMONDIR = /usr/net XDESTDIR = /usr/local X XOBJECTS = rsendd.o rsend.o XSOURCE = rsendd.c rsend.c XHEADRS = rsend.h X X# X# Possible defines in CFLAGS are: X# X# -DDEBUG when debugging the daemon, it will run attached to a terminal X# compile and load with -g instead of -O, and debug it X# -DTEST when testing the daemon, doesn't need to be root, but can't X# recieve from other machines (Entry in /etc/services not needed) X# -DINET_D if you let the daemon be invoked by inetd (the internet daemon) X# when its service is required X# -DUNIQUE if you want the user program to check rwho database for X# reciever on other hosts when not logged in on the local machine X# (i e one username is always the same person on all machines in X# the local area network) X XCFLAGS = -O -DUNIQUE X# LDFLAGS = -O # when debugging X Xall: rsend rsendd X Xrsend: rsend.o X cc ${LDFLAGS} -o rsend rsend.o X Xrsendd: rsendd.o X cc ${LDFLAGS} -o rsendd rsendd.o X Xrsend.o: ${HEADRS} X Xrsendd.o: ${HEADRS} X Xinstall: all X strip rsend rsendd X cp rsend ${DESTDIR}/rsend X cp rsendd ${DAEMONDIR}/rsendd X chmod 755 ${DESTDIR}/rsend X chmod 700 ${DAEMONDIR/rsendd X Xclean: X rm -f *.o + END-OF-FILE Makefile chmod 'u=rw,g=r,o=r' 'Makefile' echo ' -rw-r--r-- 1 zap 1138 Dec 30 23:11 Makefile (as sent)' echo -n ' ' /bin/ls -l Makefile echo 'Extracting rsend.1' sed 's/^X//' > rsend.1 << '+ END-OF-FILE rsend.1' X.TH RSEND Local "30 December 1985" X.SH NAME X.B rsend X\- send a message to another (possibly remote) user X.SH SYNOPSIS X.B rsend Xperson [ \-ttyname ] [ message ] X.SH DESCRIPTION X.I Rsend Xsends a message to another user on your machine, Xor to a user on another machine in a Xlocal area network. X.PP XIf you wish to send to someone on you own machine, then X.I person Xis just the person's login name. If you wish to send to Xa user on another host, then X.I person Xis of the form: X.sp X.in +1.5i X.I user@host X\ or\ X.I host!user X\ or X.br X.I host:user X\ or\ X.I host.user X.br X.in -1.5i X.sp XIf you don't specify a host or the username isn't logged in on your Xmachine (or doesn't even exist on your machine), the rwho\-database Xis searched for that user on the other hosts in the local area network. XIf a user is logged in several times Xthe tty with the least idletime is choosen to recieve Xthe message. X.PP X.I Rsend Xcan be used either with a message on the commandline Xor interactively. Be aware that the shell will interpret Xmetacharacters on the commandline, so it may be wise to Xquote the message, or use X.I rsend Xinteractively. X.PP XIf a message, for example ``Hello'', is supplied on the commandline X.I rsend Xwill send a message, that looks like this: X.PP X <11:47 From yourname@yourhost (yourttyname): Hello> X.PP Xand then exit. XThe message is preceded by, and followed by a newline so that the Xreciever's screen doesn't get too messed up. XThere is also a bell (Control\-G) included in the message Xto alert the recipient. X.PP XTo run X.I rsend Xinteractively omit the message from the commandline. X.I Rsend Xwill prompt ``rsend>'' and at this time the message can Xbe typed. Nothing will be sent until the first line of Xthe message is completed (unlike write(1)). XThe first line sent looks exactly as if X.I rsend Xhad been used with the message on the commandline. XThe rsend\-prompt will return and subsequent non\-empty Xlines will be sent, looking like this: X.PP X X.PP Xpreceded and followed by a newline. XTo exit X.I rsend Xtype either the EOF\-character, or `.' as the first Xand only character on a line. XIf the character `!' is typed first on a line a subshell Xwill be invoked (as specified by the environment\-variable XSHELL, or /bin/sh if SHELL is undefined). XIf the `!' is the only character on the line, the shell Xwill be interactive, otherwise the rest of the line Xwill be interpreted as a command to the shell, Xand the shell will exit after executing the command. X.PP XIf the reciever is logged in more than once, the X.I \-ttyname Xargument can be used to specify the appropriate Xterminal, e.g. X.I ``rsend foo@bar \-ttyd3'' Xor X.I ``rsend bar!foo \-d3''. XIt is also possible to replace X.I ``user'' Xwith a specification of a terminal when specifying X.I ``person'', Xe.g. X.I ``rsend \-d3'' Xor X.I ``rsend \-ttyd3@bar''. X.SH DIAGNOSTICS XIf a message couldn't be sent an appropriate Xwarning is written to the senders terminal. X.SH FILES X/etc/utmp searched for reciever X/usr/spool/rwho/whod.* searched for reciever X/bin/sh subshell if SHELL is not found X.SH "SEE ALSO" Xmesg(1), who(1), rwho(1C), talk(1), mail(1), write(1) X.SH AUTHOR XSvante Lindahl, Royal Institute of Technology, Stockholm X.SH BUGS XPossibly! + END-OF-FILE rsend.1 chmod 'u=rw,g=r,o=r' 'rsend.1' echo ' -rw-r--r-- 1 zap 3230 Dec 30 22:35 rsend.1 (as sent)' echo -n ' ' /bin/ls -l rsend.1 echo 'Extracting rsend.h' sed 's/^X//' > rsend.h << '+ END-OF-FILE rsend.h' X/* X * $Header: rsend.h,v 1.7 85/09/12 15:07:15 zap Exp $ X */ X X/* X * Revision 1.2 85/08/17 01:44:22 zap X * New format for msg - request type field added, request types can be X * CHECK (check if reciver is logged in and tty writeable) and SEND X * (go ahead and send message). X * X * Revision 1.1 85/08/16 21:42:05 zap X * Initial revision X * X * rsend.h 1.0 zap@bogart 1985-07-02 03:39:48 X */ X X/* X** Copyright (C) 1985 by Svante Lindahl. X** X** rsend - send a message to a local or remote user X** X*/ X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include "/usr/src/net/rwhod/rwhod.h" X X#define TRUE 1 X#define FALSE 0 X#define ever ;; X X#ifdef TEST X#define RENDEZ_VOUS 11147 X#endif X X#define NAME_SIZE 9 X#define TTY_SIZE 8 X#define LINE_LENGTH 80 X#define BUF_SIZE 400 X#define HOST_NAME_SIZE 32 X X#define WHDRSIZE (sizeof(wd) - sizeof (wd.wd_we)) X X/* request values */ X#define CHECK 0 X#define SEND 1 X X/* response values */ X#define SUCCESS 0 X#define NO_USER 1 X#define NOT_IN 2 X#define NOT_TTY 3 X#define WRNG_TTY 4 X#define NOT_USED 5 X#define DENIED 6 X#define OP_UTMP 7 X#define OP_TTY 8 X#define WR_FAIL 9 X#define CONFUSED 10 X#define NO_ANSW 11 X#define CHDIR 12 X X/* Seems like nr of bytes must be divisible by 4 to work between Sun and VAX */ Xstruct rsend_msg { X char rcv_name[NAME_SIZE]; /* 9 */ X char rcv_tty[TTY_SIZE]; /* 8 */ X char rqst_type; /* 1 */ X char fill[2]; /* 2 */ X char text[BUF_SIZE]; /* 400 */ X} msg; X X/* Seems like nr of bytes must be divisible by 4 to work between Sun and VAX */ Xstruct rsend_rsp { X char code; /* 1 */ X char fill[2]; /* 2 */ X char rcv_name[NAME_SIZE]; /* 9 */ X char rcv_tty[TTY_SIZE]; /* 8 */ X} rsp; X Xextern int errno; + END-OF-FILE rsend.h chmod 'u=r,g=r,o=r' 'rsend.h' echo ' -r--r--r-- 1 zap 1990 Sep 12 15:07 rsend.h (as sent)' echo -n ' ' /bin/ls -l rsend.h echo 'Extracting rsendd.c' sed 's/^X//' > rsendd.c << '+ END-OF-FILE rsendd.c' X#ifndef lint Xstatic char *RCSid = "$Header: rsendd.c,v 1.8 85/12/30 22:56:16 zap Exp $"; Xstatic char *Copyright = "rsendd.c Copyright (C) 1985 Svante Lindahl."; X#endif X X/* X * $Log: rsendd.c,v $ X * Revision 1.8 85/12/30 22:56:16 zap X * Made lint happier. X * X * Revision 1.7 85/10/06 19:50:46 zap X * Problem: daemon would occasionally get hung, fix(?): timeout after 3 X * seconds in wait and send a SIGKILL to the child. X * X * Revision 1.6 85/10/06 19:04:30 zap X * Bugfix; didn't recognize that a tty was unused, badly placed '!' removed. X * X * Revision 1.5 85/08/21 18:27:59 zap X * Better way of finding tty with least idletime. X * X * Revision 1.4 85/08/19 23:09:44 zap X * The daemon now tries to find reciver's job with least idletime if no X * particular tty is requested. X * X * Revision 1.3 85/08/19 21:10:34 zap X * Some small fixes. X * X * Revision 1.2 85/08/17 01:31:28 zap X * Checks if person is logged in and tty is writeable when called with X * request type CHECK, only writes on tty with request is set to SEND. X * X * Revision 1.1 85/08/16 21:40:20 zap X * Initial revision X * X * rsendd.c 1.0 zap@bogart 1985-07-05 18:00:30 X * X */ X X/* X** rsendd - daemon for rsend a message sending program X** X** This program, and any documentation for it, is copyrighted by Svante X** Lindahl. It may be copied for non-commercial use only, provided that X** any and all copyright notices are preserved. X** X** Please report any bugs and/or fixes to: X** X** UUCP: {seismo,mcvax,cernvax,diku,ircam,prlb2,tut,ukc,unido}!enea!ttds!zap X** ARPA: enea!ttds!zap@seismo.ARPA X** or Svante_Lindahl_NADA%QZCOM.MAILNET@MIT-MULTICS.ARPA X** X*/ X X#include "rsend.h" X X#include X#include X#include X X#ifdef INET_D X#define TIMEOUT 30 /* let the daemon time out and exit if.. */ X#define MAXIDLE 120 /* ..it is invoked by the inet-daemon */ X#endif X Xstruct sockaddr_in sin, from ; Xint fromlen = sizeof(from) ; Xextern int doit() ; X X#ifndef DEBUG Xint pid ; Xlong lastmsgtime ; X#endif X Xmain () X{ X int s, f, cc, w; X union wait status; X int (*saveintr)(), (*savequit)(); X struct passwd *getpwnam() ; X struct servent *sp ; X#ifndef DEBUG X extern void reaper(), chaser() ; X#endif X#ifdef INET_D X extern void timeout() ; X#endif X X sin.sin_addr.s_addr = INADDR_ANY ; X sin.sin_family = AF_INET ; X#ifndef TEST X if (getuid()) { X fprintf (stderr, "rsendd: not super user\n") ; X exit(1) ; X } X sp = getservbyname("rsend", "udp") ; X if (sp == NULL) { X fprintf (stderr, "rsendd: udp/rsend: unknown service\n") ; X exit(1) ; X } X sin.sin_port = sp->s_port ; X#else X sin.sin_port = htons(RENDEZ_VOUS) ; X#endif X#ifndef DEBUG X /* fork, parent quits, child dissociates itself from the tty */ X if (fork()) X _exit(0) ; X for (f = 0 ; f < 5 ; f++) X (void) close(f) ; X (void) open("/dev/null", O_RDONLY) ; X (void) open("/dev/null", O_WRONLY) ; X (void) dup(1) ; X if ((f = open("/dev/tty", O_RDWR)) > 0) { X (void) ioctl(f, (int) TIOCNOTTY, (char *) 0) ; X (void) close(f) ; X } X s = socket(AF_INET, SOCK_DGRAM, 0) ; X if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { X perror("rsendd: bind") ; X exit(1) ; X } X (void) signal(SIGCHLD, reaper) ; /* try not to leave zombies around */ X for(ever) { X cc = recvfrom(s, (char *)&msg, sizeof(msg), X 0, (struct sockaddr *) &from, &fromlen); X if ((pid = vfork()) == 0) X exit(doit(s, cc)) ; X (void) time(&lastmsgtime) ; X saveintr = signal(SIGINT, SIG_IGN) ; X savequit = signal(SIGQUIT, SIG_IGN) ; X (void) signal(SIGALRM, chaser) ; X (void) alarm(3); X while ((w = wait(&status)) != pid && w != -1) X ; X (void) signal(SIGQUIT, saveintr) ; X (void) signal(SIGQUIT, savequit) ; X#ifdef INET_D X (void) signal(SIGALRM, timeout) ; X (void) alarm(TIMEOUT) ; X#else X (void) alarm(0) ; X#endif X } X#else X s = socket(AF_INET, SOCK_DGRAM, 0) ; X if (bind(s, &sin, sizeof(sin)) < 0) { X perror("rsendd: bind") ; X exit(1) ; X } X for(ever) { X cc = recvfrom(s, (char *)&msg, sizeof(msg), 0, &from, &fromlen) ; X doit(s, cc) ; X } X#endif X} X Xint Xdoit(s, cc) X int s, cc ; X{ X int f, i, idle = -1 ; X struct stat st ; X struct utmp ut ; X struct passwd *pwp ; X long now ; X X if (cc != sizeof(msg)) { X if (cc < 0 && errno != EINTR) X perror("recvfrom") ; X return(1) ; X } X if (chdir("/dev") < 0) X rsp.code = CHDIR ; X else if (*msg.rcv_tty != '\0') { X (void) strcpy(rsp.rcv_tty, msg.rcv_tty) ; X if (stat(msg.rcv_tty, &st) < 0) X rsp.code = NOT_TTY ; X else if (((st.st_uid == 0) && (strcmp(msg.rcv_name, "root"))) X || (pwp = getpwuid(st.st_uid)) == NULL) X rsp.code = NOT_USED ; X else if (*msg.rcv_name != '\0') { X (void) strcpy(rsp.rcv_name, msg.rcv_name) ; X if (getpwnam(msg.rcv_name) == NULL) X rsp.code = NO_USER ; X else if (strcmp(msg.rcv_name, pwp->pw_name) != 0) X rsp.code = WRNG_TTY ; X else if (!(st.st_mode & 2)) X rsp.code = DENIED ; X else X rsp.code = SUCCESS ; X } else { X if (!(st.st_mode & 2)) X rsp.code = DENIED ; X else { X rsp.code = SUCCESS ; X (void) strcpy(rsp.rcv_name, pwp->pw_name) ; X } X } X } else if (*msg.rcv_name != '\0') { X (void) strcpy(rsp.rcv_name, msg.rcv_name) ; X if (getpwnam(msg.rcv_name) == NULL) X rsp.code = NO_USER ; X else if ((f = open ("/etc/utmp", 0)) < 0) X rsp.code = OP_UTMP ; X else { /* search for reciver in /etc/utmp */ X rsp.code = NOT_IN ; X (void) time(&now) ; X while (read (f, (char *)&ut, sizeof(ut)) > 0) { X if (strcmp(msg.rcv_name, ut.ut_name) == 0) { X if ((stat(ut.ut_line, &st) >= 0) && (st.st_mode & 2) X && (((i = now - st.st_mtime) < idle) || (idle = -1))) { X (void) strcpy(rsp.rcv_tty, ut.ut_line) ; X rsp.code = SUCCESS ; X idle = i ; X } else if (rsp.code != SUCCESS) { X (void) strcpy(rsp.rcv_tty, ut.ut_line) ; X rsp.code = DENIED ; X } X } X } X (void) close(f) ; X } X } else X rsp.code = CONFUSED ; /* hopefully not reached... */ X X if (rsp.code == SUCCESS) { /* found somewhere to send it */ X if ((f = open(rsp.rcv_tty, O_WRONLY)) < 0) X rsp.code = OP_TTY ; X else if (msg.rqst_type == SEND) { X if (write(f, msg.text, strlen(msg.text)) != strlen(msg.text)) X rsp.code = WR_FAIL ; X (void) close(f) ; X } X } X /* send status report to the sender */ X cc = sendto(s, (char *)&rsp, sizeof(rsp), X 0, (struct sockaddr *) &from, sizeof(from)) ; X return(cc == sizeof(rsp) ? 0 : -1) ; X} X X#ifdef INET_D Xvoid Xtimeout() X{ X long now ; X X (void) time(&now) ; X if (now - lastmsgtime >= MAXIDLE) X exit(0) ; X (void) alarm(TIMEOUT) ; X} X#endif X X#ifndef DEBUG Xvoid Xreaper() /* takes care of getting rid of zombie-children */ X{ X union wait status ; X X while (wait3(&status, WNOHANG, (struct rusage *) 0) > 0) ; X} X Xvoid Xchaser() /* chase down children that refuse to die */ X{ X (void) kill(pid, SIGKILL) ; X} X#endif + END-OF-FILE rsendd.c chmod 'u=r,g=r,o=r' 'rsendd.c' echo ' -r--r--r-- 1 zap 7552 Dec 30 22:56 rsendd.c (as sent)' echo -n ' ' /bin/ls -l rsendd.c echo 'Extracting rsend.c' sed 's/^X//' > rsend.c << '+ END-OF-FILE rsend.c' X#ifndef lint Xstatic char *RCSid = "$Header: rsend.c,v 1.7 85/12/30 22:54:02 zap Exp $"; Xstatic char *Copyright = "rsend.c Copyright (C) 1985 Svante Lindahl."; X#endif X X/* X * $Log: rsend.c,v $ X * Revision 1.7 85/12/30 22:54:02 zap X * Made lint happier. Fixed bug, replacing strcmp with strncmp. X * X * Revision 1.6 85/10/06 19:01:35 zap X * Bugfix; working directory was "wrong" when shellescape invoked, now X * does a chdir back after checking things in /dev & /usr/spool/rwho. X * X * Revision 1.5 85/08/21 17:37:12 zap X * Now chooses to send to writeable tty with least idletime if reciever X * is logged in more than once on the local host. X * X * Revision 1.4 85/08/19 23:08:56 zap X * Small change. X * X * Revision 1.3 85/08/19 21:27:51 zap X * When no host is specified and the reciever isn't logged in on the local X * host the rwho database is searched to find reciever on an other machine X * in the local area network. If logged in several times on other hosts the X * tty (pty) with the least idletime is selected. X * X * Revision 1.2 85/08/17 01:40:41 zap X * Now checks if reciever is logged in and tty is writeable before entering X * interactive mode. X * X * Revision 1.1 85/08/16 21:41:09 zap X * Initial revision X * X * rsend.c 1.0 zap@bogart 1985-07-23 21:29:27 X * X */ X X/* X** rsend - send a message to a local or remote user X** X** This program, and any documentation for it, is copyrighted by Svante X** Lindahl. It may be copied for non-commercial use only, provided that X** any and all copyright notices are preserved. X** X** Please report any bugs and/or fixes to: X** X** UUCP: {seismo,mcvax,cernvax,diku,ircam,prlb2,tut,ukc,unido}!enea!ttds!zap X** ARPA: enea!ttds!zap@seismo.ARPA X** or Svante_Lindahl_NADA%QZCOM.MAILNET@MIT-MULTICS.ARPA X** X*/ X X#include "rsend.h" X X#define TIMEOUT 2 /* seconds */ X Xint Argc, remotesend, interactive = FALSE ; Xchar **Argv, *my_name, *my_tty ; Xchar my_cpu[HOST_NAME_SIZE] ; Xchar rcv_cpu[HOST_NAME_SIZE] ; Xstruct sockaddr_in sin; X Xchar *getlogin(), *ttyname(), *rindex() ; Xstruct hostent *gethostbyname() ; Xstruct servent *getservbyname() ; X Xextern void err_check () ; Xextern void who_am_i () ; Xextern void who_is_rcvr () ; Xextern void set_up_msg() ; Xextern void set_up_tty() ; Xextern int where_is_rcvr () ; Xextern void where_to () ; Xextern void check_it () ; Xextern void be_interactive () ; Xextern void do_shell (); Xextern void build_msg () ; Xextern int build_header () ; Xextern void send_it () ; Xextern void timeout() ; Xextern void error_handle() ; X Xmain (argc, argv) X int argc ; X char **argv ; X{ X Argc = argc ; X Argv = argv ; X err_check () ; X who_am_i () ; X who_is_rcvr () ; X where_to () ; X if (interactive) { X check_it () ; X be_interactive() ; X } else { X build_msg () ; X send_it () ; X } X} X Xvoid Xerr_check () X{ X if (Argc < 2 ) { X fprintf(stderr, X "Usage: rsend user[@host] [-tty] [message...]\n") ; X exit(1) ; X } X if (!isatty(0)) { X fprintf(stderr, X "rsend: Sorry, standard input must be a tty!\n") ; X exit(1) ; X } X} X Xvoid Xwho_am_i () X{ X struct passwd *pwp ; X X my_name = getlogin() ; X if (*my_name == NULL) X if ((pwp = getpwuid(getuid())) == NULL) { X fprintf(stderr, "You are not for real!.\n") ; X exit(1) ; X } X else X my_name = (char *)pwp->pw_name ; X my_tty = rindex(ttyname(0), '/') + 1 ; X (void) gethostname(my_cpu, sizeof(my_cpu)) ; X} X Xvoid Xwho_is_rcvr () X{ X char *ptr ; X int code ; X X Argc-- ; Argv++ ; X *msg.rcv_tty = '\0' ; X *msg.rcv_name = '\0' ; X for (ptr = *Argv ; *ptr != '\0' && *ptr != '@' && *ptr != ':' X && *ptr != '!' && *ptr != '.' ; ptr++) X ; X if (*ptr == '\0') { /* no host specified */ X set_up_msg(*Argv, my_cpu) ; X if (*msg.rcv_tty == '\0') { X if ((code = where_is_rcvr()) != SUCCESS) X error_handle(code) ; X } X } else { X if (*ptr == '@') { /* user@host || -tty@host */ X *ptr++ = '\0' ; X set_up_msg(*Argv, ptr) ; X } else { /* host[.!:]user || host[.!:]-tty */ X *ptr++ = '\0' ; X set_up_msg(ptr, *Argv) ; X } X } X remotesend = strcmp(rcv_cpu, my_cpu) ; X Argc-- ; Argv++ ; X if (Argc == 0) /* anything left on the commandline? */ X interactive = TRUE ; X else if (**Argv == '-') { /* tty-specification? */ X set_up_tty(&Argv[0][1]) ; X Argc-- ; Argv++ ; X if (Argc == 0) /* still more? */ X interactive = TRUE ; X } X} X Xvoid Xset_up_msg(ptr, cpu) X char *ptr, *cpu; X{ X if (*ptr != '-') /* reciever's username or tty? */ X (void) strcpy(msg.rcv_name, ptr) ; X else X set_up_tty(++ptr) ; X (void) strcpy(rcv_cpu, cpu) ; X} X Xvoid Xset_up_tty(ptr) X char *ptr ; X{ X if (!strcmp("console", ptr)) X (void) strcpy(msg.rcv_tty, ptr) ; X else { X if (strncmp("tty", ptr, 3)) X (void) strcpy(msg.rcv_tty, "tty") ; X (void) strcat(msg.rcv_tty, ptr) ; X } X msg.rcv_tty[TTY_SIZE-1] = '\0' ; X} X Xint Xwhere_is_rcvr() X{ X int code, f, i, idle = -1, nowd = 0 ; X char path[1024], *getwd() ; X struct stat st ; X struct utmp ut ; X long now ; X X if (getwd(path) == '\0') X nowd++ ; X if (getpwnam(msg.rcv_name) == NULL) X code = NO_USER ; X else if ((f = open ("/etc/utmp", 0)) < 0) X code = OP_UTMP ; X else if (chdir("/dev") < 0) X code = CHDIR ; X else { X code = NOT_IN ; /* search for reciver in /etc/utmp */ X (void) time(&now) ; X while (read(f, (char *)&ut, sizeof(ut)) > 0) { X if (strncmp (msg.rcv_name, ut.ut_name, 8) == 0) { X if ((stat(ut.ut_line, &st) >= 0) && (st.st_mode & 2) X && (((i = now - st.st_mtime) < idle) || (idle = -1))) { X (void) strcpy(msg.rcv_tty, ut.ut_line) ; X code = SUCCESS ; X idle = i ; X } else if (code != SUCCESS) { X (void) strcpy(msg.rcv_tty, ut.ut_line) ; X code = DENIED ; X } X } X } X (void) close(f) ; X } X#ifdef UNIQUE X { X int cc, n ; X DIR *dirp ; X struct direct *dp ; X struct whoent *we ; X struct whod wd ; X register struct whod *wp = &wd ; X X if ( ((code == NO_USER) || (code == NOT_IN) || (code == CHDIR)) X && (chdir("/usr/spool/rwho") >= 0) X && ((dirp = opendir(".")) != NULL)) { X /* check other machines in local network (use rwho database) */ X (void) time(&now) ; X while (dp = readdir (dirp)) { X if ((dp->d_ino == 0) || (strncmp(dp->d_name, "whod.", 5))) X continue ; X if ((f = open(dp->d_name, 0)) < 0) X continue ; X cc = read(f, (char *)&wd, sizeof(struct whod)) ; X if ((cc < WHDRSIZE) || (now - wp->wd_recvtime > 5 * 60) X || (!strcmp (my_cpu, wp->wd_hostname))) { X (void) close (f) ; X continue ; X } X for(we = wp->wd_we, X n = (cc - WHDRSIZE) / sizeof(struct whoent); X n > 0; n--, we++) { X if (strncmp(msg.rcv_name, we->we_utmp.out_name, 8)) X continue ; X if ((i = we->we_idle) < idle || idle == -1) { X code = SUCCESS ; X idle = i ; X (void) strcpy(rcv_cpu, wp->wd_hostname) ; X (void) strcpy(msg.rcv_tty, we->we_utmp.out_line) ; X } X } X (void) close (f) ; X } X if (code == SUCCESS) X fprintf(stderr, "%s not logged in on %s, sending to %s@%s\n", X msg.rcv_name, my_cpu, msg.rcv_name, rcv_cpu) ; X } X } X#endif X if (!nowd) X (void) chdir(path) ; X return(code) ; X} X Xvoid Xwhere_to() X{ X struct hostent *hp ; X struct servent *sp ; X X hp = gethostbyname(rcv_cpu) ; /* get address of reciever's machine */ X if (hp == (struct hostent *) 0 ) { X fprintf(stderr, "rsend: I don't know a machine named %s\n", rcv_cpu) ; X exit(1) ; X } X bzero(&sin, sizeof (sin)) ; X bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length) ; X sin.sin_family = hp->h_addrtype ; X#ifndef TEST X sp = getservbyname("rsend", "udp") ; /* find daemon's port */ X if (sp == NULL) { X fprintf(stderr, "rsend isn't supported by this machine\n") ; X exit(1) ; X } X sin.sin_port = sp->s_port ; X#else X if (remotesend) { /* remote sends not supported when testing */ X fprintf(stderr, X "Sorry, only local sends are supported at this time\n") ; X exit(1) ; X } X sin.sin_port = htons(RENDEZ_VOUS) ; /* find daemon's port */ X#endif X} X Xvoid Xcheck_it() X{ X msg.rqst_type = CHECK ; X send_it() ; X msg.rqst_type = SEND ; X} X Xvoid Xbe_interactive() X{ X int hdr_siz, i ; X char buf[BUF_SIZE] ; X X hdr_siz = build_header () ; /* standard header for first message */ X msg.text[hdr_siz++] = ' ' ; X msg.text[hdr_siz] = '\0' ; X for(ever) { X printf("rsend>") ; /* prompt for input */ X (void) fflush(stdout) ; X if (fgets(buf, sizeof(buf), stdin) != NULL) { X if ((i = strlen(buf)) > 1) { X buf[--i] = '\0' ; /* nullify the \n from fgets */ X if (i == 1 && *buf == '.') /* '.' to exit pgm */ X break ; X else if (*buf == '!') X do_shell(i, buf) ; X else { X if (hdr_siz + i > LINE_LENGTH) /* separate header if .. */ X (void) strcat(msg.text, "\r\n") ; /* .. long message */ X if (hdr_siz + i + 8 > BUF_SIZE) { /* too much... */ X (void) strncat(msg.text, buf, BUF_SIZE-hdr_siz-i-8) ; X (void) strcat(msg.text, "...\r\n") ; X fprintf(stderr, X "rsend: long message, %d word(s) truncated\n", X Argc + 1) ; X } else { X (void) strcat(msg.text, buf) ; X (void) strcat(msg.text, ">\r\n") ; X } X msg.text[BUF_SIZE-1] = '\0' ; X send_it() ; X (void) sprintf(msg.text, "\r\n<%s: ", my_name) ; X hdr_siz = strlen(msg.text) ; X } X } X } else { X printf("\r\n") ; X break ; X } X } X} X Xvoid Xdo_shell (i, cmd) X int i ; X char *cmd ; X{ X int pid, w ; X union wait status ; X char *sh, *getenv() ; X int (*saveintr)(), (*savequit)() ; X X if ((sh = getenv("SHELL")) == NULL) X sh = "/bin/sh" ; X if (i == 1) { /* interactive shell */ X if ((pid = vfork()) == 0) X execl(sh, sh, (char *) 0) ; X } X else if ((pid = vfork ()) == 0) /* do one commandline */ X execl(sh, sh, "-c", ++cmd, (char *) 0) ; X saveintr = signal(SIGINT, SIG_IGN) ; X savequit = signal(SIGQUIT, SIG_IGN) ; X while ((w = wait(&status)) != pid && w != -1) X ; X if (i == 1) X printf("\n") ; X (void) signal(SIGINT, saveintr) ; X (void) signal(SIGQUIT, savequit) ; X} X Xvoid Xbuild_msg () X{ X int hdr_siz, too_much = FALSE ; X char buf[BUF_SIZE] ; X X msg.rqst_type = SEND ; X hdr_siz = build_header() ; X while (Argc--) { /* copy everything from the commandline */ X (void) strcat(buf, " ") ; X if (hdr_siz + strlen(buf) + 8 + strlen(*Argv) > BUF_SIZE ) { X too_much = TRUE ; X break ; X } X (void) strcat(buf, *Argv++) ; X } X if (hdr_siz + strlen(buf) > LINE_LENGTH) /* separate heading from .. */ X (void) strcat(msg.text, "\r\n") ; /* ..message if it's long */ X (void) strcat(msg.text, buf) ; X if (too_much) { X (void) strcat(msg.text, "...\r\n") ; X fprintf(stderr, X "rsend: long message, %d word(s) truncated\n", Argc + 1) ; X } X else X (void) strcat(msg.text, ">\r\n") ; X msg.text[BUF_SIZE-1] = '\0' ; /* just to make sure...*/ X} X Xint Xbuild_header () X{ X long clock ; X struct tm *tp, *localtime() ; X X (void) time(&clock) ; /* message header: "<11:47 From foo@bar (ttyxx):" */ X tp = localtime(&clock) ; X (void) sprintf(msg.text,"\r\n\007<%d:%02d From %s@%s (%s):", X tp->tm_hour, tp->tm_min, my_name, my_cpu, my_tty) ; X return(strlen(msg.text)) ; X} X Xvoid Xsend_it () X{ X struct sockaddr from ; X int s, cc, fromlen = sizeof(from) ; X X s = socket(AF_INET, SOCK_DGRAM, 0) ; /* get a socket */ X if (s < 0) { X perror("rsend: socket") ; X exit(1) ; X } X /* send to address in 'sin' through socket to (possibly remote) daemon */ X cc = sendto(s, (char *)&msg, sizeof(msg), X 0, (struct sockaddr *)&sin, sizeof (sin)) ; X if (cc != sizeof(msg)) { X perror("sendto") ; X exit(1) ; X } X rsp.code = NO_ANSW ; X (void) signal(SIGALRM, timeout) ;/* will not wait forever for a response */ X (void) alarm(TIMEOUT) ; X (void) recvfrom(s, (char *)&rsp, sizeof(rsp), 0, &from, &fromlen) ; X (void) close(s) ; X (void) alarm(0) ; /* don't time out on an interactive user */ X if (*msg.rcv_name == '\0') X (void) strcpy(msg.rcv_name, rsp.rcv_name) ; X if (*msg.rcv_tty == '\0') X (void) strcpy(msg.rcv_tty, rsp.rcv_tty) ; X if (rsp.code != SUCCESS) X error_handle(rsp.code) ; X} X Xvoid Xtimeout () X{ X fprintf(stderr, "rsend: timeout before acknowledged by rsend-daemon@%s\n", X rcv_cpu) ; X exit(0) ; X} X Xvoid Xerror_handle (errcod) X int errcod ; X{ X char errmsg[BUF_SIZE] ; X char recvr[NAME_SIZE + HOST_NAME_SIZE + 1] ; X char daemon_at[HOST_NAME_SIZE + 15] ; X X if (remotesend) { X (void) sprintf(recvr, "%s@%s", msg.rcv_name, rcv_cpu) ; X (void) sprintf(daemon_at, "rsend-daemon@%s", rcv_cpu) ; X (void) strcat(rcv_cpu, ":") ; X } else { X (void) strcpy(recvr, msg.rcv_name) ; X (void) strcpy(daemon_at, "rsend-daemon") ; X *rcv_cpu = '\0' ; X } X X switch (errcod) { X case NO_USER : X (void) sprintf(errmsg, "%s: no such user", recvr) ; X break ; X case NOT_IN : X (void) sprintf(errmsg, "%s not logged in", recvr) ; X break ; X case NOT_TTY : X (void) sprintf(errmsg, "%s/dev/%s no such device", X rcv_cpu, msg.rcv_tty) ; X break ; X case WRNG_TTY : X (void) sprintf(errmsg, "%s is not on %s", recvr, msg.rcv_tty) ; X break ; X case NOT_USED : X (void) sprintf(errmsg, "%s/dev/%s is not in use", X rcv_cpu, msg.rcv_tty) ; X break ; X case DENIED : X (void) sprintf(errmsg, X "%s/dev/%s: permission denied, try mail instead", X rcv_cpu, msg.rcv_tty) ; X break ; X case OP_UTMP : X (void) sprintf(errmsg, "cannot open %s/etc/utmp", rcv_cpu) ; X break ; X case OP_TTY : X (void) sprintf(errmsg, "cannot open %s/dev/%s", rcv_cpu, msg.rcv_tty) ; X break ; X case WR_FAIL : X (void) sprintf(errmsg, "couldn't write on %s/dev/%s", X rcv_cpu, msg.rcv_tty) ; X break ; X case CONFUSED : X (void) sprintf(errmsg, "%s is confused", daemon_at) ; X break ; X case NO_ANSW : X (void) sprintf(errmsg, "no answer from %s", daemon_at) ; X break ; X case CHDIR : X (void) sprintf(errmsg, "couldn't chdir to /dev") ; X break ; X default : X (void) sprintf(errmsg, X "this cannot happen! (unknown error, no:%d)", errcod) ; X } X fprintf(stderr, "rsend: %s\n", errmsg) ; X exit(1) ; X} + END-OF-FILE rsend.c chmod 'u=r,g=r,o=r' 'rsend.c' echo ' -r--r--r-- 1 zap 15648 Dec 30 22:55 rsend.c (as sent)' echo -n ' ' /bin/ls -l rsend.c exit 0 -- Svante Lindahl, NADA, KTH Numerical Analysis & Computer Science Phone: +46-8-787-7146 Royal Institute of Technology UUCP: {seismo,mcvax}!enea!ttds!zap ARPA: enea!ttds!zap@seismo.CSS.GOV