2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
12 * Copyright (c) 1980 Regents of the University of California.
13 * All rights reserved.
15 * Redistribution and use in source and binary forms are permitted provided
16 * that: (1) source distributions retain this entire copyright notice and
17 * comment, and (2) distributions including binaries display the following
18 * acknowledgement: ``This product includes software developed by the
19 * University of California, Berkeley and its contributors'' in the
20 * documentation or other materials provided with the distribution and in
21 * all advertising materials mentioning features or use of this software.
22 * Neither the name of the University nor the names of its contributors may
23 * be used to endorse or promote products derived from this software without
24 * specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include <sys/types.h>
34 #include <sys/socket.h>
42 #include <netinet/in.h>
45 #include <sys/ttold.h>
49 #include <sys/param.h> /* for MAXHOSTNAMELEN */
52 #include <sys/ioctl.h>
64 #define UTMPX_FILE "/var/log/utmpx"
65 #endif /* UTMPX_FILE */
68 #define dsyslog if (debug) syslog
70 struct sockaddr_in sin
= { AF_INET
};
72 char hostname
[MAXHOSTNAMELEN
];
73 struct utmpx
*utmp
= NULL
;
76 unsigned utmpmtime
= 0; /* last modification time for utmp */
77 unsigned utmpsize
= 0; /* last malloced size for utmp */
84 #define rindex strrchr
86 #define signal(s, f) sigset((s), (f))
89 #define sigmask(m) (1 << ((m)-1))
92 #define set2mask(setp) ((setp)->__sigbits[0])
93 #define mask2set(mask, setp) \
94 ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
103 (void) sigprocmask(0, (sigset_t
*)0, &nset
);
104 mask2set(mask
, &nset
);
105 (void) sigprocmask(SIG_SETMASK
, &nset
, &oset
);
106 return (set2mask(&oset
));
116 (void) sigprocmask(0, (sigset_t
*)0, &nset
);
117 mask2set(mask
, &nset
);
118 (void) sigprocmask(SIG_BLOCK
, &nset
, &oset
);
119 return (set2mask(&oset
));
126 #define NAMLEN (sizeof (uts[0].ut_name) + 1)
128 void jkfprintf(FILE *tp
, char *name
, int mbox
, int offset
);
129 void mailfor(char *name
);
130 void notify(struct utmpx
*utp
, int offset
);
131 void onalrm(int sig
);
141 struct sockaddr_in from
;
148 openlog("comsat", 0, LOG_DAEMON
);
150 while ((c
= getopt(argc
, argv
, "d")) != -1) {
156 syslog(LOG_ERR
, "invalid argument %s", argv
[optind
]);
161 /* verify proper invocation */
162 fromlen
= (socklen_t
)sizeof (from
);
163 if (getsockname(0, (struct sockaddr
*)&from
, &fromlen
) < 0) {
164 fprintf(stderr
, "%s: ", argv
[0]);
165 perror("getsockname");
172 chdir("/var/spool/mail");
174 if ((uf
= open(UTMPX_FILE
, O_RDONLY
)) < 0) {
175 syslog(LOG_ERR
, "%s: %m", UTMPX_FILE
);
176 (void) recv(0, msgbuf
, sizeof (msgbuf
) - 1, 0);
179 (void) time(&lastmsgtime
);
180 (void) gethostname(hostname
, sizeof (hostname
));
182 (void) signal(SIGALRM
, onalrm
);
183 (void) signal(SIGTTOU
, SIG_IGN
);
185 (void) signal(SIGCHLD
, reapchildren
);
187 (void) signal(SIGCHLD
, SIG_IGN
); /* no zombies */
190 cc
= recv(0, msgbuf
, sizeof (msgbuf
) - 1, 0);
197 if (nutmp
== 0) /* no users (yet) */
199 sigblock(sigmask(SIGALRM
));
201 (void) time(&lastmsgtime
);
211 while (wait3(NULL
, WNOHANG
, NULL
) > 0)
224 if ((ulong_t
)now
- (ulong_t
)lastmsgtime
>= MAXIDLE
)
226 dsyslog(LOG_DEBUG
, "alarm\n");
229 if (statbf
.st_mtime
> utmpmtime
) {
230 dsyslog(LOG_DEBUG
, " changed\n");
231 utmpmtime
= statbf
.st_mtime
;
232 if (statbf
.st_size
> utmpsize
) {
233 utmpsize
= statbf
.st_size
+ 10 * sizeof (struct utmpx
);
235 utmp
= (struct utmpx
*)realloc(utmp
, utmpsize
);
237 utmp
= (struct utmpx
*)malloc(utmpsize
);
239 dsyslog(LOG_DEBUG
, "malloc failed\n");
244 nutmp
= read(uf
, utmp
, statbf
.st_size
)/sizeof (struct utmpx
);
246 dsyslog(LOG_DEBUG
, " ok\n");
253 struct utmpx
*utp
= &utmp
[nutmp
];
259 * Don't bother doing anything if nobody is
260 * logged into the system.
262 if (utmp
== NULL
|| nutmp
== 0)
264 dsyslog(LOG_DEBUG
, "mailfor %s\n", name
);
266 while (*cp
&& *cp
!= '@')
269 dsyslog(LOG_DEBUG
, "bad format\n");
274 while (--utp
>= utmp
)
275 if ((utp
->ut_type
== USER_PROCESS
) &&
276 (!strncmp(utp
->ut_name
, name
, sizeof (utmp
[0].ut_name
))))
287 struct sgttyb gttybuf
;
288 char tty
[sizeof (utmp
[0].ut_line
) + 5];
289 char name
[sizeof (utmp
[0].ut_name
) + 1];
290 struct stat stb
, stl
;
291 struct utimbuf timep
;
296 strcpy(tty
, "/dev/");
297 strncat(tty
, utp
->ut_line
, sizeof (utp
->ut_line
));
298 dsyslog(LOG_DEBUG
, "notify %s on %s\n", utp
->ut_name
, tty
);
299 if (stat(tty
, &stb
) == -1) {
300 dsyslog(LOG_DEBUG
, "can't stat tty\n");
303 if ((stb
.st_mode
& 0100) == 0) {
304 dsyslog(LOG_DEBUG
, "wrong mode\n");
309 signal(SIGALRM
, SIG_DFL
);
312 strncpy(name
, utp
->ut_name
, sizeof (utp
->ut_name
));
313 name
[sizeof (name
) - 1] = '\0';
316 * Do all operations that check protections as the user who
317 * will be getting the biff.
319 if ((pwd
= getpwnam(name
)) == (struct passwd
*)-1) {
320 dsyslog(LOG_DEBUG
, "getpwnam failed\n");
323 if (setuid(pwd
->pw_uid
) == -1) {
324 dsyslog(LOG_DEBUG
, "setuid failed\n");
329 * We need to make sure that the tty listed in the utmp
330 * file really is a tty device so that a corrupted utmp
331 * file doesn't cause us to over-write a real file.
333 if ((fd
= open(tty
, O_RDWR
)) == -1) {
334 dsyslog(LOG_DEBUG
, "can't open tty");
337 if (isatty(fd
) == 0) {
338 dsyslog(LOG_DEBUG
, "line listed in utmp file is not a tty\n");
343 * For the case where the user getting the biff is root,
344 * we need to make sure that the tty we will be sending
345 * the biff to is also owned by root.
347 * Check after open, to prevent race on open.
350 if (fstat(fd
, &stb
) != 0 || stb
.st_uid
!= pwd
->pw_uid
) {
352 "tty is not owned by user getting the biff\n");
357 * Prevent race by doing fdopen on fd, not fopen
358 * Fopen opens w/ O_CREAT, which is dangerous too
360 if ((tp
= fdopen(fd
, "w")) == 0) {
361 dsyslog(LOG_DEBUG
, "fdopen failed\n");
365 if (ioctl(fd
, TIOCGETP
, >tybuf
) == -1) {
366 dsyslog(LOG_DEBUG
, "ioctl TIOCGETP failed\n");
369 cr
= (gttybuf
.sg_flags
&CRMOD
) && !(gttybuf
.sg_flags
&RAW
) ? "" : "\r";
370 fprintf(tp
, "%s\n\007New mail for %s@%.*s\007 has arrived:%s\n",
371 cr
, name
, sizeof (hostname
), hostname
, cr
);
372 fprintf(tp
, "----%s\n", cr
);
374 if ((mbox
= open(name
, O_RDONLY
)) == -1) {
375 dsyslog(LOG_DEBUG
, "can't open mailbox for %s", name
);
379 * In case of a worldwritable mail spool directory, we must take
380 * care we don't open and read from the wrong file.
382 if (fstat(mbox
, &stb
) == -1 || lstat(name
, &stl
) == -1) {
383 dsyslog(LOG_DEBUG
, "stat() failed on mail file\n");
388 * Here we make sure that the file wasn't a hardlink or softlink
389 * while we opened it and that it wasn't changed afterwards
391 if (!S_ISREG(stl
.st_mode
) ||
392 stl
.st_dev
!= stb
.st_dev
||
393 stl
.st_ino
!= stb
.st_ino
||
394 stl
.st_uid
!= pwd
->pw_uid
||
396 dsyslog(LOG_DEBUG
, "mail spool file must be plain file\n");
400 timep
.actime
= stb
.st_atime
;
401 timep
.modtime
= stb
.st_mtime
;
402 jkfprintf(tp
, name
, mbox
, offset
);
408 jkfprintf(tp
, name
, mbox
, offset
)
414 register int linecnt
, charcnt
;
418 dsyslog(LOG_DEBUG
, "HERE %s's mail starting at %d\n",
420 if ((fi
= fdopen(mbox
, "r")) == NULL
) {
421 dsyslog(LOG_DEBUG
, "Cant read the mail\n");
425 fseek(fi
, offset
, L_SET
);
428 * Print the first 7 lines or 560 characters of the new mail
429 * (whichever comes first). Skip header crap other than
430 * From, Subject, To, and Date.
437 while (fgets(line
, sizeof (line
), fi
) != NULL
) {
443 if (linecnt
<= 0 || charcnt
<= 0) {
444 fprintf(tp
, "...more...%s\n", cr
);
447 if (strncmp(line
, "From ", 5) == 0)
449 if (inheader
&& (line
[0] == ' ' || line
[0] == '\t'))
451 cp
= index(line
, ':');
452 if (cp
== 0 || (index(line
, ' ') && index(line
, ' ') < cp
))
457 strncmp(line
, "Date", cnt
) &&
458 strncmp(line
, "From", cnt
) &&
459 strncmp(line
, "Subject", cnt
) &&
460 strncmp(line
, "To", cnt
))
462 cp
= index(line
, '\n');
466 for (i
= strlen(line
); i
-- > 0; )
467 if (!isprint(line
[i
]))
471 fprintf(tp
, "%s%s\n", line
, cr
);
472 linecnt
--, charcnt
-= strlen(line
);
474 fprintf(tp
, "----%s\n", cr
);