home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #ifndef lint
- char copyright[] =
- "@(#) Copyright (c) 1980 Regents of the University of California.\n\
- All rights reserved.\n";
- #endif not lint
-
- #ifndef lint
- static char sccsid[] = "@(#)wall.c 5.3 (Berkeley) 4/20/86";
- #endif not lint
-
- /*
- * wall.c - Broadcast a message to all users.
- *
- * This program is not related to David Wall, whose Stanford Ph.D. thesis
- * is entitled "Mechanisms for Broadcast and Selective Broadcast".
- */
- #ifdef UW
- /*
- * Modified Monday 19 November 10:52:26 CST 1990 by Dean Luick
- * Added a check to see if an xwall message failed. If so, then it will
- * try a tty ring on the least idle tty for that display.
- *
- * Modified Wed April 5 14:24:19 CST 1989 by Lexie Panter, The
- * University of Wisconsin-Madison. Checks utmp entries for
- * X display names in host part. If someone is running X, send
- * the message via xwall instead of sending the message to all of
- * their existing windows.
- *
- * bolo@cat (Joe Burger) on Thu Aug 10 12:27:17 CDT 1989
- * Fixed "bug" with X windows code. A non-terminated string was
- * causing problems.
- * Security enhanced: only writes message to character devices.
- *
- * Modified Thu Jan 14 12:57:17 CST 1988 by Dave Cohrs
- * Added a "-u username" option to broadcast only to tty's logged
- * in as the given username. A compromise between wall and write.
- */
- #endif UW
-
- #include <stdio.h>
- #include <errno.h>
- #include <signal.h>
- #if defined(UW) && defined(CRAY)
- #include <time.h>
- #include <sys/sysmacros.h>
- #else
- #include <sys/time.h>
- #endif
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <utmp.h>
- #ifdef UW
- #include <pwd.h>
- #include <strings.h>
- #endif UW
-
- #if defined(UW) && defined(CRAY)
- extern char *index();
- extern char *rindex();
- #endif
-
- /*
- * Systems derived from SysVr3 (like UNICOS) define the
- * fcntl flags differently so pick up the dropped ball here.
- */
- #ifndef FNDELAY
- #define FNDELAY O_NDELAY
- #endif
-
- #if defined(UW) && !defined(XWALL)
- #define XWALL "/usr/misc/X11/bin/xwall"
- #endif
-
- #define IGNOREUSER "sleeper"
-
- char hostname[32];
- char mesg[3000];
- int msize,sline;
- struct utmp *utmp;
- char *strcpy();
- char *strcat();
- char *malloc();
- char who[9] = "???";
- #ifdef UW
- long now; /* time() is defined in sys/time.h */
- #else
- long now, time();
- #endif
- struct tm *localtime();
- struct tm *localclock;
- #ifdef UW
- void _exit();
- char xmesg[3000];
- int xmsize;
- int xdisplay();
- char *exmalloc();
- struct xlist { /* keeps a list of displays */
- char display[sizeof(utmp->ut_host)+2]; /* to send an xwall message.*/
- char user[sizeof(utmp->ut_name)+1];
- char tty[sizeof(utmp->ut_line)+1];
- long idle;
- struct xlist *next;
- } *xlist;
- #endif UW
-
- extern errno;
-
- main(argc, argv)
- char *argv[];
- {
- register int i, c;
- register struct utmp *p;
- int f;
- struct stat statb;
- #ifdef UW
- struct passwd *pw;
- register char *onlyuser = 0;
- #endif UW
-
- (void) gethostname(hostname, sizeof (hostname));
- if ((f = open("/etc/utmp", O_RDONLY, 0)) < 0) {
- fprintf(stderr, "Cannot open /etc/utmp\n");
- exit(1);
- }
- #ifdef UW
- if(argc >= 2 && argv[1][0] == '-') {
- i = strlen(argv[1]);
-
- if(strncmp(argv[1], "-u", 2) == 0 && (i > 2 || argc >= 3)) {
- if(i == 2) {
- onlyuser = argv[2];
- argv++;
- argc--;
- } else
- onlyuser = &argv[1][2];
- argv++;
- argc--;
- } else {
- fprintf(stderr, "Usage: wall [ -u username ] [ file ]\n");
- exit(1);
- }
- }
- #endif UW
- now = time( 0 );
- localclock = localtime( &now );
- sline = ttyslot(); /* 'utmp' slot no. of sender */
- (void) fstat(f, &statb);
- #ifdef UW
- utmp = (struct utmp *)exmalloc(statb.st_size);
- #else
- utmp = (struct utmp *)malloc(statb.st_size);
- #endif
- c = read(f, (char *)utmp, statb.st_size);
- (void) close(f);
- c /= sizeof(struct utmp);
- if (sline)
- strncpy(who, utmp[sline].ut_name, sizeof(utmp[sline].ut_name));
- #ifdef UW
- else if((pw = getpwuid(getuid())) != NULL)
- (void) strncpy(who,pw->pw_name,8);
- if (sline == 0)
- (void) sprintf(mesg,
- "\r\n\07\07\07Broadcast Message from %s@%s at %d:%02d ...\r\n\n"
- , who
- , hostname
- , localclock -> tm_hour
- , localclock -> tm_min
- );
- else
- #endif UW
- sprintf(mesg,
- "\r\n\007\007Broadcast Message from %s@%s (%.*s) at %d:%02d ...\r\n\n"
- , who
- , hostname
- , sizeof(utmp[sline].ut_line)
- , utmp[sline].ut_line
- , localclock -> tm_hour
- , localclock -> tm_min
- );
- msize = strlen(mesg);
- if (argc >= 2) {
- /* take message from unix file instead of standard input */
- if (freopen(argv[1], "r", stdin) == NULL) {
- perror(argv[1]);
- exit(1);
- }
- }
- while ((i = getchar()) != EOF) {
- if (i == '\n')
- mesg[msize++] = '\r';
- if (msize >= sizeof mesg) {
- fprintf(stderr, "Message too long\n");
- exit(1);
- }
- mesg[msize++] = i;
- #ifdef UW
- xmesg[xmsize++] = i;
- #endif UW
- }
- fclose(stdin);
- for (i=0; i<c; i++) {
- p = &utmp[i];
- if (p->ut_name[0] == 0 ||
- #ifdef UW
- (onlyuser != 0 &&
- strncmp(p->ut_name, onlyuser, sizeof(p->ut_name)) != 0) ||
- #ifdef nonuser
- /* support for the Sun nonuser macro */
- nonuser(*p) ||
- #endif nonuser
- #endif UW
- strncmp(p->ut_name, IGNOREUSER, sizeof(p->ut_name)) == 0)
- continue;
- #ifdef UW
- /* Only send wall message to ttys not using X */
- if (!xdisplay(p))
- #endif UW
- sendmes(p->ut_line);
- }
- #ifdef UW
- sendwindows();
- #endif UW
- exit(0);
- }
-
- sendmes(tty)
- char *tty;
- {
- register f, flags;
- static char t[50] = "/dev/";
- int e, i;
- #ifdef UW
- struct stat st;
- #endif
-
- strcpy(t + 5, tty);
-
- #ifdef UW
- /* make sure the file is a tty ! */
- if (stat(t, &st) == -1 || (st.st_mode&S_IFMT) != S_IFCHR)
- return;
- #endif
- if ((f = open(t, O_WRONLY|O_NDELAY)) < 0) {
- if (errno != EWOULDBLOCK)
- perror(t);
- return;
- }
- if ((flags = fcntl(f, F_GETFL, 0)) == -1) {
- perror(t);
- return;
- }
- if (fcntl(f, F_SETFL, flags | FNDELAY) == -1)
- goto oldway;
- i = write(f, mesg, msize);
- e = errno;
- (void) fcntl(f, F_SETFL, flags);
- if (i == msize) {
- (void) close(f);
- return;
- }
- #ifdef UW
- /*
- * The write can succeed, but not write all characters
- * may be written. We should retry, but are too lazy
- * -- see write(2) about non-blocking IO.
- * bolo
- */
- if (i < 0 && e != EWOULDBLOCK) {
- #else
- if (e != EWOULDBLOCK) {
- #endif
- errno = e;
- perror(t);
- (void) close(f);
- return;
- }
- oldway:
- while ((i = fork()) == -1)
- if (wait((int *)0) == -1) {
- fprintf(stderr, "Try again\n");
- return;
- }
- if (i) {
- (void) close(f);
- return;
- }
-
- (void) write(f, mesg, msize);
- exit(0);
- }
-
- #ifdef UW
-
- /* Add display entry to the xlist. */
- static int
- xdisplay(p)
- struct utmp *p;
- {
- struct xlist *xptr;
- struct stat ttystat, p0stat;
- char display[sizeof(p->ut_host)+2]; /* may need to add '0' and '\0'. */
- char xpty[64]; /* name of the pty */
- char *colonp, *dotp, *displayp;
- int n;
- long idle;
-
- static int get_major = 1; /* get the major number of a pty */
- static int have_major; /* we have the major number of a pty */
- static int pty_major; /* pty major number */
-
-
- /* If the host field contains the char ':', the person is using X. */
- strncpy(display, p->ut_host, sizeof(p->ut_host));
- if ((colonp = index(display, ':')) != NULL) {
- /* display string may be truncated at bad place.
- * If ends with ':', assume display 0.
- * If ends with '.', remove the '.'. */
- n = strlen(display)-1;
- if (display[n] == ':') {
- display[n+1] = '0';
- display[n+2] = '\0';
- } else if (display[n] == '.') display[n] = '\0';
-
- /* We are interested in the display, screen is irrelevant */
- if (dotp = index(colonp, '.')) *dotp = '\0';
- /* Recognize that hostname:0 is equivalent to :0 */
- if (strncmp(display, hostname, strlen(hostname)) == 0)
- displayp = colonp;
- else
- displayp = display;
-
- /*
- * Since this is an X window, we are either running on a pty or are
- * the X device. Make sure we pick the least idle pty in case
- * the X operation fails.
- */
- sprintf(xpty,"/dev/%s", p->ut_line);
- if (stat(xpty,&ttystat) == 0) {
- /*
- * If we can't write to the xpty or it is not a character special,
- * then our idle time is infinite (-2).
- */
- if (! ((ttystat.st_mode & 020) &&
- ((ttystat.st_mode & S_IFMT) == S_IFCHR)))
- idle = -2;
- else {
- /*
- * Get a major number of a known pty.
- */
- if (get_major) {
- get_major = 0; /* only try to get major once */
-
- #ifndef CRAY
- if (stat("/dev/ttyp0",&p0stat) == 0) {
- #else !CRAY
- if (stat("/dev/ttyp000",&p0stat) == 0) {
- #endif CRAY
- have_major = 1;
- pty_major = major(p0stat.st_rdev);
- } else
- perror("ttyp0 stat:");
- }
-
- /*
- * If we have a pty major number and it matches this
- * tty, then we know we have a pty.
- */
- if (have_major && pty_major == major(ttystat.st_rdev)) {
- /* Get idle time */
- idle = (long) now - (long) ttystat.st_atime;
- if (idle < 0L) idle = 0L;
- } else
- idle = -1; /* pick another if we can */
- }
- } else {
- perror("stat:");
- idle = -2;
- }
-
- /* Search the X list for a matching display. */
- for (xptr = xlist; xptr != NULL; xptr = xptr->next) {
- if (strcmp(xptr->display, displayp) == 0) {
- /*
- * We have matched displays. We always want to take the
- * least idle tty. If the chosen tty (xptr) is unknown
- * (-2) then use the new one. If we couldn't get the chosen
- * tty's major number or it didn't match a pty (-1) and
- * the new one has a valid idle itme, then use the new one
- * as if it is known (!= -2). If the chosen tty has an idle
- * time and the new one is less, use the new tty.
- */
- if ( xptr->idle == -2 ||
- (xptr->idle == -1 && idle != -2 ) ||
- (xptr->idle >= 0 && idle >= 0 && idle < xptr->idle) ) {
- #ifdef DEBUG
- fprintf(stderr,
- "%s@%s: switching from %s to %s, idle %d\n",
- xptr->user, xptr->display,
- xptr->tty, p->ut_line, (int) idle);
- #endif
- goto new_tty; /* an evil goto */
- }
-
- break;
- }
- }
- if (xptr == NULL) {
- xptr = (struct xlist *) exmalloc(sizeof(struct xlist));
- xptr->next = xlist;
- xlist = xptr;
- strcpy(xptr->display, displayp);
- strncpy(xptr->user, p->ut_name, sizeof(p->ut_name));
- #ifdef DEBUG
- fprintf(stderr, "%s@%s: new entry on %s, idle %d\n",
- xptr->user, xptr->display,
- p->ut_line, (int) idle);
- #endif
- new_tty:
- strncpy(xptr->tty, p->ut_line, sizeof(p->ut_line));
- xptr->idle = idle;
- }
- return 1;
- }
-
- return 0;
- }
-
- sendwindows()
- {
- struct xlist *xptr;
- FILE *xfp; /* file pointer for xwall command */
- char xcomm[128]; /* xwall command */
- int status; /* pclose() status */
- int fs; /* fork status */
-
- #ifdef DEBUG
- fprintf(stderr,"\n");
- #endif
-
- for (xptr = xlist; xptr != NULL; xptr = xptr->next) {
- #ifdef DEBUG
- fprintf(stderr,"Trying %s@%s on %s\n",
- xptr->user, xptr->display ,xptr->tty);
- #endif
- sprintf(xcomm,"%s -user %s -display %s",XWALL,
- xptr->user, xptr->display);
-
- if ((fs = fork()) == 0) {
- /*
- * We want to fork because xwall will wait 15 seconds
- * before deciding that it can't connect to the remote
- * xwalld.
- */
- if (xfp = popen(xcomm,"w")) {
- if ((int) fwrite(xmesg,1,xmsize,xfp) != xmsize) {
- fprintf(stderr, "wall: fwrite to pipe of '%s' failed\n",
- xcomm);
- goto tty_wall;
- }
- /* pclose() returns the result of a wait() */
- status = (pclose(xfp) >> 8) & 0xff; /* get return status */
- if (status) {
- fprintf(stderr, "wall: '%s' failed, return code %d\n",
- xcomm, status);
- goto tty_wall;
- }
- } else {
- fprintf(stderr, "wall: popen failed on '%s'\n", xcomm);
- tty_wall:
- sendmes(xptr->tty);
-
- }
- _exit(0);
- } else if (fs < 0)
- perror("fork");
- }
-
- /*
- * Wait for all children to finish.
- */
- while ((int) wait((union wait *)0) != -1) ;
- }
-
- char *
- exmalloc(size)
- int size;
- {
- char *ret;
-
- if ((ret = malloc(size)) == 0) {
- fprintf(stderr, "Out of memory\n");
- exit(1);
- }
- return (ret);
- }
- #endif UW
-