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>
61 #define UTMPX_FILE "/etc/utmpx"
62 #endif /* UTMPX_FILE */
65 #define dsyslog if (debug) syslog
67 struct sockaddr_in sin
= { AF_INET
};
69 char hostname
[MAXHOSTNAMELEN
];
70 struct utmpx
*utmp
= NULL
;
73 unsigned utmpmtime
= 0; /* last modification time for utmp */
74 unsigned utmpsize
= 0; /* last malloced size for utmp */
81 #define rindex strrchr
83 #define signal(s, f) sigset((s), (f))
86 #define sigmask(m) (1 << ((m)-1))
89 #define set2mask(setp) ((setp)->__sigbits[0])
90 #define mask2set(mask, setp) \
91 ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
100 (void) sigprocmask(0, (sigset_t
*)0, &nset
);
101 mask2set(mask
, &nset
);
102 (void) sigprocmask(SIG_SETMASK
, &nset
, &oset
);
103 return (set2mask(&oset
));
113 (void) sigprocmask(0, (sigset_t
*)0, &nset
);
114 mask2set(mask
, &nset
);
115 (void) sigprocmask(SIG_BLOCK
, &nset
, &oset
);
116 return (set2mask(&oset
));
123 #define NAMLEN (sizeof (uts[0].ut_name) + 1)
125 void jkfprintf(FILE *tp
, char *name
, int mbox
, int offset
);
126 void mailfor(char *name
);
127 void notify(struct utmpx
*utp
, int offset
);
128 void onalrm(int sig
);
138 struct sockaddr_in from
;
145 openlog("comsat", 0, LOG_DAEMON
);
147 while ((c
= getopt(argc
, argv
, "d")) != -1) {
153 syslog(LOG_ERR
, "invalid argument %s", argv
[optind
]);
158 /* verify proper invocation */
159 fromlen
= (socklen_t
)sizeof (from
);
160 if (getsockname(0, (struct sockaddr
*)&from
, &fromlen
) < 0) {
161 fprintf(stderr
, "%s: ", argv
[0]);
162 perror("getsockname");
169 chdir("/var/spool/mail");
171 if ((uf
= open(UTMPX_FILE
, O_RDONLY
)) < 0) {
172 syslog(LOG_ERR
, "%s: %m", UTMPX_FILE
);
173 (void) recv(0, msgbuf
, sizeof (msgbuf
) - 1, 0);
176 (void) time(&lastmsgtime
);
177 (void) gethostname(hostname
, sizeof (hostname
));
179 (void) signal(SIGALRM
, onalrm
);
180 (void) signal(SIGTTOU
, SIG_IGN
);
182 (void) signal(SIGCHLD
, reapchildren
);
184 (void) signal(SIGCHLD
, SIG_IGN
); /* no zombies */
187 cc
= recv(0, msgbuf
, sizeof (msgbuf
) - 1, 0);
194 if (nutmp
== 0) /* no users (yet) */
196 sigblock(sigmask(SIGALRM
));
198 (void) time(&lastmsgtime
);
208 while (wait3(NULL
, WNOHANG
, NULL
) > 0)
221 if ((ulong_t
)now
- (ulong_t
)lastmsgtime
>= MAXIDLE
)
223 dsyslog(LOG_DEBUG
, "alarm\n");
226 if (statbf
.st_mtime
> utmpmtime
) {
227 dsyslog(LOG_DEBUG
, " changed\n");
228 utmpmtime
= statbf
.st_mtime
;
229 if (statbf
.st_size
> utmpsize
) {
230 utmpsize
= statbf
.st_size
+ 10 * sizeof (struct utmpx
);
232 utmp
= (struct utmpx
*)realloc(utmp
, utmpsize
);
234 utmp
= (struct utmpx
*)malloc(utmpsize
);
236 dsyslog(LOG_DEBUG
, "malloc failed\n");
241 nutmp
= read(uf
, utmp
, statbf
.st_size
)/sizeof (struct utmpx
);
243 dsyslog(LOG_DEBUG
, " ok\n");
250 struct utmpx
*utp
= &utmp
[nutmp
];
256 * Don't bother doing anything if nobody is
257 * logged into the system.
259 if (utmp
== NULL
|| nutmp
== 0)
261 dsyslog(LOG_DEBUG
, "mailfor %s\n", name
);
263 while (*cp
&& *cp
!= '@')
266 dsyslog(LOG_DEBUG
, "bad format\n");
271 while (--utp
>= utmp
)
272 if ((utp
->ut_type
== USER_PROCESS
) &&
273 (!strncmp(utp
->ut_name
, name
, sizeof (utmp
[0].ut_name
))))
284 struct sgttyb gttybuf
;
285 char tty
[sizeof (utmp
[0].ut_line
) + 5];
286 char name
[sizeof (utmp
[0].ut_name
) + 1];
287 struct stat stb
, stl
;
293 strcpy(tty
, "/dev/");
294 strncat(tty
, utp
->ut_line
, sizeof (utp
->ut_line
));
295 dsyslog(LOG_DEBUG
, "notify %s on %s\n", utp
->ut_name
, tty
);
296 if (stat(tty
, &stb
) == -1) {
297 dsyslog(LOG_DEBUG
, "can't stat tty\n");
300 if ((stb
.st_mode
& 0100) == 0) {
301 dsyslog(LOG_DEBUG
, "wrong mode\n");
306 signal(SIGALRM
, SIG_DFL
);
309 strncpy(name
, utp
->ut_name
, sizeof (utp
->ut_name
));
310 name
[sizeof (name
) - 1] = '\0';
313 * Do all operations that check protections as the user who
314 * will be getting the biff.
316 if ((pwd
= getpwnam(name
)) == (struct passwd
*)-1) {
317 dsyslog(LOG_DEBUG
, "getpwnam failed\n");
320 if (setuid(pwd
->pw_uid
) == -1) {
321 dsyslog(LOG_DEBUG
, "setuid failed\n");
326 * We need to make sure that the tty listed in the utmp
327 * file really is a tty device so that a corrupted utmp
328 * file doesn't cause us to over-write a real file.
330 if ((fd
= open(tty
, O_RDWR
)) == -1) {
331 dsyslog(LOG_DEBUG
, "can't open tty");
334 if (isatty(fd
) == 0) {
335 dsyslog(LOG_DEBUG
, "line listed in utmp file is not a tty\n");
340 * For the case where the user getting the biff is root,
341 * we need to make sure that the tty we will be sending
342 * the biff to is also owned by root.
344 * Check after open, to prevent race on open.
347 if (fstat(fd
, &stb
) != 0 || stb
.st_uid
!= pwd
->pw_uid
) {
349 "tty is not owned by user getting the biff\n");
354 * Prevent race by doing fdopen on fd, not fopen
355 * Fopen opens w/ O_CREAT, which is dangerous too
357 if ((tp
= fdopen(fd
, "w")) == 0) {
358 dsyslog(LOG_DEBUG
, "fdopen failed\n");
362 if (ioctl(fd
, TIOCGETP
, >tybuf
) == -1) {
363 dsyslog(LOG_DEBUG
, "ioctl TIOCGETP failed\n");
366 cr
= (gttybuf
.sg_flags
&CRMOD
) && !(gttybuf
.sg_flags
&RAW
) ? "" : "\r";
367 fprintf(tp
, "%s\n\007New mail for %s@%.*s\007 has arrived:%s\n",
368 cr
, name
, sizeof (hostname
), hostname
, cr
);
369 fprintf(tp
, "----%s\n", cr
);
371 if ((mbox
= open(name
, O_RDONLY
)) == -1) {
372 dsyslog(LOG_DEBUG
, "can't open mailbox for %s", name
);
376 * In case of a worldwritable mail spool directory, we must take
377 * care we don't open and read from the wrong file.
379 if (fstat(mbox
, &stb
) == -1 || lstat(name
, &stl
) == -1) {
380 dsyslog(LOG_DEBUG
, "stat() failed on mail file\n");
385 * Here we make sure that the file wasn't a hardlink or softlink
386 * while we opened it and that it wasn't changed afterwards
388 if (!S_ISREG(stl
.st_mode
) ||
389 stl
.st_dev
!= stb
.st_dev
||
390 stl
.st_ino
!= stb
.st_ino
||
391 stl
.st_uid
!= pwd
->pw_uid
||
393 dsyslog(LOG_DEBUG
, "mail spool file must be plain file\n");
397 timep
[0] = stb
.st_atime
;
398 timep
[1] = stb
.st_mtime
;
399 jkfprintf(tp
, name
, mbox
, offset
);
405 jkfprintf(tp
, name
, mbox
, offset
)
411 register int linecnt
, charcnt
;
415 dsyslog(LOG_DEBUG
, "HERE %s's mail starting at %d\n",
417 if ((fi
= fdopen(mbox
, "r")) == NULL
) {
418 dsyslog(LOG_DEBUG
, "Cant read the mail\n");
422 fseek(fi
, offset
, L_SET
);
425 * Print the first 7 lines or 560 characters of the new mail
426 * (whichever comes first). Skip header crap other than
427 * From, Subject, To, and Date.
434 while (fgets(line
, sizeof (line
), fi
) != NULL
) {
440 if (linecnt
<= 0 || charcnt
<= 0) {
441 fprintf(tp
, "...more...%s\n", cr
);
444 if (strncmp(line
, "From ", 5) == 0)
446 if (inheader
&& (line
[0] == ' ' || line
[0] == '\t'))
448 cp
= index(line
, ':');
449 if (cp
== 0 || (index(line
, ' ') && index(line
, ' ') < cp
))
454 strncmp(line
, "Date", cnt
) &&
455 strncmp(line
, "From", cnt
) &&
456 strncmp(line
, "Subject", cnt
) &&
457 strncmp(line
, "To", cnt
))
459 cp
= index(line
, '\n');
463 for (i
= strlen(line
); i
-- > 0; )
464 if (!isprint(line
[i
]))
468 fprintf(tp
, "%s%s\n", line
, cr
);
469 linecnt
--, charcnt
-= strlen(line
);
471 fprintf(tp
, "----%s\n", cr
);