xref: /original-bsd/libexec/comsat/comsat.c (revision 0958d343)
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.25 (Berkeley) 06/22/92";
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 <signal.h>
27 #include <sgtty.h>
28 #include <utmp.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <syslog.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <paths.h>
38 
39 int	debug = 0;
40 #define	dsyslog	if (debug) syslog
41 
42 #define MAXIDLE	120
43 
44 char	hostname[MAXHOSTNAMELEN];
45 struct	utmp *utmp = NULL;
46 time_t	lastmsgtime;
47 int	nutmp, uf;
48 
49 void jkfprintf __P((FILE *, char[], off_t));
50 void mailfor __P((char *));
51 void notify __P((struct utmp *, off_t));
52 void onalrm __P((int));
53 void reapchildren __P((int));
54 
55 int
56 main(argc, argv)
57 	int argc;
58 	char *argv[];
59 {
60 	struct sockaddr_in from;
61 	register int cc;
62 	int fromlen;
63 	char msgbuf[100];
64 
65 	/* verify proper invocation */
66 	fromlen = sizeof(from);
67 	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
68 		(void)fprintf(stderr,
69 		    "comsat: getsockname: %s.\n", strerror(errno));
70 		exit(1);
71 	}
72 	openlog("comsat", LOG_PID, LOG_DAEMON);
73 	if (chdir(_PATH_MAILDIR)) {
74 		syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR);
75 		exit(1);
76 	}
77 	if ((uf = open(_PATH_UTMP, O_RDONLY, 0)) < 0) {
78 		syslog(LOG_ERR, ".main: %s: %m", _PATH_UTMP);
79 		(void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
80 		exit(1);
81 	}
82 	(void)time(&lastmsgtime);
83 	(void)gethostname(hostname, sizeof(hostname));
84 	onalrm(0);
85 	(void)signal(SIGALRM, onalrm);
86 	(void)signal(SIGTTOU, SIG_IGN);
87 	(void)signal(SIGCHLD, reapchildren);
88 	for (;;) {
89 		cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
90 		if (cc <= 0) {
91 			if (errno != EINTR)
92 				sleep(1);
93 			errno = 0;
94 			continue;
95 		}
96 		if (!nutmp)		/* no one has logged in yet */
97 			continue;
98 		sigblock(sigmask(SIGALRM));
99 		msgbuf[cc] = 0;
100 		(void)time(&lastmsgtime);
101 		mailfor(msgbuf);
102 		sigsetmask(0L);
103 	}
104 }
105 
106 void
107 reapchildren(signo)
108 	int signo;
109 {
110 	while (wait3(NULL, WNOHANG, NULL) > 0);
111 }
112 
113 void
114 onalrm(signo)
115 	int signo;
116 {
117 	static u_int utmpsize;		/* last malloced size for utmp */
118 	static u_int utmpmtime;		/* last modification time for utmp */
119 	struct stat statbf;
120 
121 	if (time(NULL) - lastmsgtime >= MAXIDLE)
122 		exit(0);
123 	(void)alarm((u_int)15);
124 	(void)fstat(uf, &statbf);
125 	if (statbf.st_mtime > utmpmtime) {
126 		utmpmtime = statbf.st_mtime;
127 		if (statbf.st_size > utmpsize) {
128 			utmpsize = statbf.st_size + 10 * sizeof(struct utmp);
129 			if ((utmp = realloc(utmp, utmpsize)) == NULL) {
130 				syslog(LOG_ERR, "%s", strerror(errno));
131 				exit(1);
132 			}
133 		}
134 		(void)lseek(uf, (off_t)0, L_SET);
135 		nutmp = read(uf, utmp, (int)statbf.st_size)/sizeof(struct utmp);
136 	}
137 }
138 
139 void
140 mailfor(name)
141 	char *name;
142 {
143 	register struct utmp *utp = &utmp[nutmp];
144 	register char *cp;
145 	off_t offset;
146 
147 	if (!(cp = index(name, '@')))
148 		return;
149 	*cp = '\0';
150 	offset = atoi(cp + 1);
151 	while (--utp >= utmp)
152 		if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name)))
153 			notify(utp, offset);
154 }
155 
156 static char *cr;
157 
158 void
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, "%s: %s", tty, strerror(errno));
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 void
197 jkfprintf(tp, name, offset)
198 	register FILE *tp;
199 	char name[];
200 	off_t offset;
201 {
202 	register char *cp, ch;
203 	register FILE *fi;
204 	register int linecnt, charcnt, inheader;
205 	char line[BUFSIZ];
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