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