/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: wall.c 1.6 90/04/06 $@(#)wall:wall.c	1.13"

#define	FAILURE	(-1)
#define	TRUE	1
#define	FALSE	0

#include <signal.h>

char	mesg[3000];

#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utmp.h>
#include <sys/utsname.h>
#include <dirent.h>
#include <pwd.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>

extern	unsigned alarm();
extern	unsigned short getuid();
int	entries;
extern	int errno;
extern	char *sys_errlist[];
extern	void exit();
extern	void endgrent();
char	*infile;
int	group;
struct	group *pgrp;
extern	struct group *getgrnam();
char	*grpname;
char	line[MAXNAMLEN+1] = "???";
extern	void perror();
extern	char *strcpy();
extern	char *strncpy();
char	*systm;
char	*timstr;
long	tloc;
extern	time_t time();
extern	char *ttyname();
unsigned int usize;
struct	utmp *utmp;
struct	utsname utsn;
char	who[9]	= "???";

#define equal(a,b)		(!strcmp( (a), (b) ))

main(argc, argv)
int	argc;
char	*argv[];
{
	int	i=0, fd;
	register	struct utmp *p;
	FILE	*f;
	struct	stat statbuf;
	register	char *ptr;
	struct	passwd *pwd;
	extern	struct passwd *getpwuid();
	char	*malloc();
	char 	msgid[16];

	if(uname(&utsn) == -1) {
	sprintf(msgid,"syserr:%d",errno+1);
	fprintf(stderr, gettxt("wall:1", "wall: uname() failed, %s\n"), gettxt(msgid, sys_errlist[errno]));
		exit(2);
	}
	systm = utsn.nodename;

	if(stat(UTMP_FILE, &statbuf) < 0) {
		fprintf(stderr, gettxt("wall:2", "stat failed on %s\n"), UTMP_FILE);
		exit(1);
	}
	/*
		get usize (an unsigned int) for malloc call
 		and check that there is no truncation (for those 16 bit CPUs)
 	*/
	usize = statbuf.st_size;
	if(usize != statbuf.st_size) {
		fprintf(stderr, gettxt("wall:3", "'%s' too big.\n"), UTMP_FILE);
		exit(1);
	}
	entries = usize / sizeof(struct utmp);
	if((utmp=(struct utmp *)malloc(usize)) == NULL) {
		fprintf(stderr, gettxt("wall:4", "cannot allocate memory for '%s'.\n"), UTMP_FILE);
		exit(1);
	}

	if((fd=open(UTMP_FILE, O_RDONLY)) < 0) {
		fprintf(stderr,gettxt("wall:5", "cannot open '%s'\n"), UTMP_FILE);
		exit(1);
	}
	if(read(fd, (char *) utmp, usize) != usize) {
		fprintf(stderr, gettxt("wall:6", "cannot read '%s'\n"), UTMP_FILE);
		exit(1);
	}
	close(fd);
	readargs(argc, argv);

	/*
		Get the name of the terminal wall is running from.
	*/
	strcpy(line, ttyname(fileno(stderr)) + sizeof("/dev/")-1);

	if (who[0] == '?') {
		if (pwd = getpwuid((int) getuid()))
			strncpy(&who[0],pwd->pw_name,sizeof(who));
	}
	f = stdin;
	if(infile) {
		f = fopen(infile, "r");
		if(f == NULL) {
			fprintf(stderr,"%s??\n", infile);
			exit(1);
		}
	}
	for(ptr= &mesg[0]; fgets(ptr,&mesg[sizeof(mesg)]-ptr, f) != NULL
		; ptr += strlen(ptr));
	fclose(f);
	time(&tloc);
	timstr = ctime(&tloc);
	for(i=0;i<entries;i++) {
		if((p=(&utmp[i]))->ut_type != USER_PROCESS) continue;
		sendmes(p);
	}
	alarm(60);
	do {
		i = wait((int *)0);
	} while(i != -1 || errno != ECHILD);
	exit(0); /* NOTREACHED */
}

sendmes(p)
struct utmp *p;
{
	register i;
	register char *s;
	static char device[] = "/dev/123456789012";
	FILE *f;
	int fd;
	char user[sizeof(p->ut_user)+1];

	strncpy(user, p->ut_user, sizeof(p->ut_user));
	user[sizeof(p->ut_user)] = '\0';
	if(group)
		if(!chkgrp(user))
			return;
	while((i=fork()) == -1) {
		alarm(60);
		wait((int *)0);
		alarm(0);
	}

	if(i)
		return;

	signal(SIGHUP, SIG_IGN);
	alarm(60);
	s = &device[0];
	strncpy(&s[sizeof("/dev/")-1], &p->ut_line[0], sizeof(p->ut_line));
#ifdef DEBUG
	f = fopen("wall.debug", "a");
#else
	/*
	 * The open of the terminal line can fail if an entry exists
	 * in utmp, but the device node does not exist.  This should
	 * never happen, but since fopen() does an implicit O_CREAT,
	 * it is safer to do the open with O_WRONLY so an IFREG file
	 * doesn't get created.
	 */
	if ((fd = open(s, O_WRONLY)) < 0) {
		fprintf(stderr,
			gettxt("wall:7", "Cannot send to %.-8s on %s\n"),
			user, s);
		exit(1);
	}
	f = fdopen(fd, "w");
#endif
	if(f == NULL) {
		fprintf(stderr,gettxt("wall:7", "Cannot send to %.-8s on %s\n"),  user, s);
		perror("open");
		exit(1);
	}
	fprintf(f, "\07\07\07Broadcast Message from %s (%s) on %s %19.19s",
		 who, line, systm, timstr);
	if(group)
		fprintf(f, " to group %s", grpname);
	fprintf(f, "...\n");
#ifdef DEBUG
	fprintf(f,"DEBUG: To %.8s on %s\n", user, s);
#endif
	fprintf(f, "%s\n", mesg);
	fclose(f);
	exit(0);
}

readargs(ac, av)
int ac;
char **av;
{
	register int i;

	for(i = 1; i < ac; i++) {
		if(equal(av[i], "-g")) {
			if(group) {
				fprintf(stderr, gettxt("wall:8", "Only one group allowed\n"));
				exit(1);
			}
			i++;
			if((pgrp=getgrnam(grpname= av[i])) == NULL) {
				fprintf(stderr, gettxt("wall:9", "Unknown group %s\n"), grpname);
				exit(1);
			}
			endgrent();
			group++;
		}
		else
			infile = av[i];
	}
}
#define BLANK		' '
chkgrp(name)
register char *name;
{
	register int i;
	register char *p;

	for(i = 0; pgrp->gr_mem[i] && pgrp->gr_mem[i][0]; i++) {
		for(p=name; *p && *p != BLANK; p++);
		*p = 0;
		if(equal(name, pgrp->gr_mem[i]))
			return(1);
	}

	return(0);
}
