xref: /csrg-svn/libexec/comsat/comsat.c (revision 42663)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)comsat.c	5.20 (Berkeley) 06/01/90";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <sys/file.h>
22 #include <sys/wait.h>
23 
24 #include <netinet/in.h>
25 
26 #include <stdio.h>
27 #include <sgtty.h>
28 #include <utmp.h>
29 #include <signal.h>
30 #include <errno.h>
31 #include <netdb.h>
32 #include <syslog.h>
33 #include <string.h>
34 #include <ctype.h>
35 
36 #include "pathnames.h"
37 
38 /*
39  * comsat
40  */
41 int	debug = 0;
42 #define	dsyslog	if (debug) syslog
43 
44 #define MAXIDLE	120
45 
46 char	hostname[MAXHOSTNAMELEN];
47 struct	utmp *utmp = NULL;
48 time_t	lastmsgtime, time();
49 int	nutmp, uf;
50 
51 /* ARGSUSED */
52 main(argc, argv)
53 	int argc;
54 	char **argv;
55 {
56 	extern int errno;
57 	register int cc;
58 	char msgbuf[100];
59 	struct sockaddr_in from;
60 	int fromlen;
61 	void onalrm(), reapchildren();
62 
63 	/* verify proper invocation */
64 	fromlen = sizeof(from);
65 	if (getsockname(0, &from, &fromlen) < 0) {
66 		(void)fprintf(stderr,
67 		    "comsat: getsockname: %s.\n", strerror(errno));
68 		exit(1);
69 	}
70 	openlog("comsat", LOG_PID, LOG_DAEMON);
71 	if (chdir(_PATH_MAIL)) {
72 		syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAIL);
73 		exit(1);
74 	}
75 	if ((uf = open(_PATH_UTMP, O_RDONLY, 0)) < 0) {
76 		syslog(LOG_ERR, ".main: %s: %m", _PATH_UTMP);
77 		(void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
78 		exit(1);
79 	}
80 	(void)time(&lastmsgtime);
81 	(void)gethostname(hostname, sizeof(hostname));
82 	onalrm();
83 	(void)signal(SIGALRM, onalrm);
84 	(void)signal(SIGTTOU, SIG_IGN);
85 	(void)signal(SIGCHLD, reapchildren);
86 	for (;;) {
87 		cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
88 		if (cc <= 0) {
89 			if (errno != EINTR)
90 				sleep(1);
91 			errno = 0;
92 			continue;
93 		}
94 		if (!nutmp)		/* no one has logged in yet */
95 			continue;
96 		sigblock(sigmask(SIGALRM));
97 		msgbuf[cc] = 0;
98 		(void)time(&lastmsgtime);
99 		mailfor(msgbuf);
100 		sigsetmask(0L);
101 	}
102 }
103 
104 void
105 reapchildren()
106 {
107 	while (wait3((union wait *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
108 }
109 
110 void
111 onalrm()
112 {
113 	static u_int utmpsize;		/* last malloced size for utmp */
114 	static u_int utmpmtime;		/* last modification time for utmp */
115 	struct stat statbf;
116 	off_t lseek();
117 	char *malloc(), *realloc();
118 
119 	if (time((time_t *)NULL) - lastmsgtime >= MAXIDLE)
120 		exit(0);
121 	(void)alarm((u_int)15);
122 	(void)fstat(uf, &statbf);
123 	if (statbf.st_mtime > utmpmtime) {
124 		utmpmtime = statbf.st_mtime;
125 		if (statbf.st_size > utmpsize) {
126 			utmpsize = statbf.st_size + 10 * sizeof(struct utmp);
127 			if (utmp)
128 				utmp = (struct utmp *)realloc((char *)utmp, utmpsize);
129 			else
130 				utmp = (struct utmp *)malloc(utmpsize);
131 			if (!utmp) {
132 				syslog(LOG_ERR, "malloc failed");
133 				exit(1);
134 			}
135 		}
136 		(void)lseek(uf, 0L, L_SET);
137 		nutmp = read(uf, utmp, (int)statbf.st_size)/sizeof(struct utmp);
138 	}
139 }
140 
141 mailfor(name)
142 	char *name;
143 {
144 	register struct utmp *utp = &utmp[nutmp];
145 	register char *cp;
146 	off_t offset;
147 
148 	if (!(cp = index(name, '@')))
149 		return;
150 	*cp = '\0';
151 	offset = atoi(cp + 1);
152 	while (--utp >= utmp)
153 		if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name)))
154 			notify(utp, offset);
155 }
156 
157 static char *cr;
158 
159 notify(utp, offset)
160 	register struct utmp *utp;
161 	off_t offset;
162 {
163 	static char tty[20] = _PATH_DEV;
164 	struct sgttyb gttybuf;
165 	struct stat stb;
166 	FILE *tp;
167 	char name[sizeof(utmp[0].ut_name) + 1];
168 
169 	(void)strncpy(tty + sizeof(_PATH_DEV) - 1, utp->ut_line,
170 	    sizeof(utp->ut_line));
171 	if (stat(tty, &stb) || !(stb.st_mode & S_IEXEC)) {
172 		dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_name, tty);
173 		return;
174 	}
175 	dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty);
176 	if (fork())
177 		return;
178 	(void)signal(SIGALRM, SIG_DFL);
179 	(void)alarm((u_int)30);
180 	if ((tp = fopen(tty, "w")) == NULL) {
181 		dsyslog(LOG_ERR, "fopen of tty %s failed", tty);
182 		_exit(-1);
183 	}
184 	(void)ioctl(fileno(tp), TIOCGETP, &gttybuf);
185 	cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ?
186 	    "\n" : "\n\r";
187 	(void)strncpy(name, utp->ut_name, sizeof(utp->ut_name));
188 	name[sizeof(name) - 1] = '\0';
189 	(void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived:%s----%s",
190 	    cr, name, sizeof(hostname), hostname, cr, cr);
191 	jkfprintf(tp, name, offset);
192 	(void)fclose(tp);
193 	_exit(0);
194 }
195 
196 jkfprintf(tp, name, offset)
197 	register FILE *tp;
198 	char name[];
199 	off_t offset;
200 {
201 	register char *cp, ch;
202 	register FILE *fi;
203 	register int linecnt, charcnt, inheader;
204 	char line[BUFSIZ];
205 	off_t fseek();
206 
207 	if ((fi = fopen(name, "r")) == NULL)
208 		return;
209 	(void)fseek(fi, offset, L_SET);
210 	/*
211 	 * Print the first 7 lines or 560 characters of the new mail
212 	 * (whichever comes first).  Skip header crap other than
213 	 * From, Subject, To, and Date.
214 	 */
215 	linecnt = 7;
216 	charcnt = 560;
217 	inheader = 1;
218 	while (fgets(line, sizeof(line), fi) != NULL) {
219 		if (inheader) {
220 			if (line[0] == '\n') {
221 				inheader = 0;
222 				continue;
223 			}
224 			if (line[0] == ' ' || line[0] == '\t' ||
225 			    strncmp(line, "From:", 5) &&
226 			    strncmp(line, "Subject:", 8))
227 				continue;
228 		}
229 		if (linecnt <= 0 || charcnt <= 0) {
230 			(void)fprintf(tp, "...more...%s", cr);
231 			return;
232 		}
233 		/* strip weird stuff so can't trojan horse stupid terminals */
234 		for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) {
235 			ch = toascii(ch);
236 			if (!isprint(ch) && !isspace(ch))
237 				ch |= 0x40;
238 			(void)fputc(ch, tp);
239 		}
240 		(void)fputs(cr, tp);
241 		--linecnt;
242 	}
243 	(void)fprintf(tp, "----%s\n", cr);
244 }
245