2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * Modified for Linux, Mon Mar 8 18:16:24 1993, faith@cs.unc.edu
37 * Wed Jun 22 21:41:56 1994, faith@cs.unc.edu:
38 * Added fix from Mike Grupenhoff (kashmir@umiacs.umd.edu)
39 * Mon Jul 1 17:01:39 MET DST 1996, janl@math.uio.no:
40 * - Added fix from David.Chapell@mail.trincoll.edu enabling daemons
42 * - ANSIed it since I was working on it anyway.
43 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
44 * - added Native Language Support
57 #include <sys/param.h>
63 #include "carefulputc.h"
64 #include "closestream.h"
69 static sig_atomic_t signal_received
= 0;
71 struct write_control
{
73 const char *src_login
;
75 const char *dst_login
;
76 char dst_tty
[PATH_MAX
];
79 static void __attribute__ ((__noreturn__
)) usage(FILE * out
)
81 fputs(USAGE_HEADER
, out
);
83 _(" %s [options] <user> [<ttyname>]\n"),
84 program_invocation_short_name
);
86 fputs(USAGE_SEPARATOR
, out
);
87 fputs(_("Send a message to another user.\n"), out
);
89 fputs(USAGE_OPTIONS
, out
);
90 fputs(USAGE_HELP
, out
);
91 fputs(USAGE_VERSION
, out
);
93 fprintf(out
, USAGE_MAN_TAIL("write(1)"));
94 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
98 * check_tty - check that a terminal exists, and get the message bit
101 static int check_tty(char *tty
, int *tty_writeable
, time_t *tty_atime
, int showerror
)
106 if (strlen(tty
) + 6 > sizeof(path
))
108 sprintf(path
, "/dev/%s", tty
);
109 if (stat(path
, &s
) < 0) {
114 if (getuid() == 0) /* root can always write */
117 *tty_writeable
= (s
.st_mode
& S_IWGRP
) && (getegid() == s
.st_gid
);
119 *tty_atime
= s
.st_atime
;
124 * check_utmp - checks that the given user is actually logged in on
127 static int check_utmp(const struct write_control
*ctl
)
132 utmpname(_PATH_UTMP
);
135 while ((u
= getutent())) {
136 if (strncmp(ctl
->dst_login
, u
->ut_user
, sizeof(u
->ut_user
)) == 0 &&
137 strncmp(ctl
->dst_tty
, u
->ut_line
, sizeof(u
->ut_line
)) == 0) {
148 * search_utmp - search utmp for the "best" terminal to write to
150 * Ignores terminals with messages disabled, and of the rest, returns
151 * the one with the most recent access time. Returns as value the number
152 * of the user's terminals with messages enabled, or -1 if the user is
153 * not logged in at all.
155 * Special case for writing to yourself - ignore the terminal you're
156 * writing from, unless that's the only terminal with messages enabled.
158 static void search_utmp(struct write_control
*ctl
)
161 time_t best_atime
= 0, tty_atime
;
162 int num_ttys
= 0, valid_ttys
= 0, tty_writeable
= 0, user_is_me
= 0;
164 utmpname(_PATH_UTMP
);
167 while ((u
= getutent())) {
168 if (strncmp(ctl
->dst_login
, u
->ut_user
, sizeof(u
->ut_user
)) == 0) {
170 if (check_tty(u
->ut_line
, &tty_writeable
, &tty_atime
, 0))
173 if (ctl
->src_uid
&& !tty_writeable
)
174 /* skip ttys with msgs off */
176 if (strcmp(u
->ut_line
, ctl
->src_tty
) == 0) {
178 /* don't write to yourself */
181 if (u
->ut_type
!= USER_PROCESS
)
182 /* it's not a valid entry */
185 if (tty_atime
> best_atime
) {
186 best_atime
= tty_atime
;
187 xstrncpy(ctl
->dst_tty
, u
->ut_line
, sizeof(ctl
->dst_tty
));
194 errx(EXIT_FAILURE
, _("%s is not logged in"), ctl
->dst_login
);
195 if (valid_ttys
== 0) {
197 /* ok, so write to yourself! */
198 xstrncpy(ctl
->dst_tty
, ctl
->src_tty
, sizeof(ctl
->dst_tty
));
201 errx(EXIT_FAILURE
, _("%s has messages disabled"), ctl
->dst_login
);
202 } else if (valid_ttys
> 1) {
203 warnx(_("%s is logged in more than once; writing to %s"),
204 ctl
->dst_login
, ctl
->dst_tty
);
210 * signal_handler - cause write loop to exit
212 static void signal_handler(int signo
)
214 signal_received
= signo
;
218 * write_line - like fputs(), but makes control characters visible and
219 * turns \n into \r\n.
221 static void write_line(char *s
)
225 #define PUTC(c) if (fputc_careful(c, stdout, '^') == EOF) \
226 err(EXIT_FAILURE, _("carefulputc failed"));
238 * do_write - actually make the connection
240 static void do_write(const struct write_control
*ctl
)
242 char *login
, *pwuid
, *time_stamp
;
245 char path
[PATH_MAX
], *host
, line
[512];
246 struct sigaction sigact
;
248 /* Determine our login name(s) before the we reopen() stdout */
249 if ((pwd
= getpwuid(ctl
->src_uid
)) != NULL
)
250 pwuid
= pwd
->pw_name
;
253 if ((login
= getlogin()) == NULL
)
256 if (strlen(ctl
->dst_tty
) + 6 > sizeof(path
))
257 errx(EXIT_FAILURE
, _("tty path %s too long"), ctl
->dst_tty
);
258 snprintf(path
, sizeof(path
), "/dev/%s", ctl
->dst_tty
);
259 if ((freopen(path
, "w", stdout
)) == NULL
)
260 err(EXIT_FAILURE
, "%s", path
);
262 sigact
.sa_handler
= signal_handler
;
263 sigemptyset(&sigact
.sa_mask
);
265 sigaction(SIGINT
, &sigact
, NULL
);
266 sigaction(SIGHUP
, &sigact
, NULL
);
269 host
= xgethostname();
271 host
= xstrdup("???");
273 now
= time((time_t *) NULL
);
274 time_stamp
= ctime(&now
);
275 time_stamp
[16] = '\0';
276 printf("\r\n\007\007\007");
277 if (strcmp(login
, pwuid
))
278 printf(_("Message from %s@%s (as %s) on %s at %s ..."),
279 login
, host
, pwuid
, ctl
->src_tty
, time_stamp
+ 11);
281 printf(_("Message from %s@%s on %s at %s ..."),
282 login
, host
, ctl
->src_tty
, time_stamp
+ 11);
286 while (fgets(line
, sizeof(line
), stdin
) != NULL
) {
294 int main(int argc
, char **argv
)
296 int tty_writeable
= 0, src_fd
, c
;
297 struct write_control ctl
= { 0 };
299 static const struct option longopts
[] = {
300 {"version", no_argument
, NULL
, 'V'},
301 {"help", no_argument
, NULL
, 'h'},
305 setlocale(LC_ALL
, "");
306 bindtextdomain(PACKAGE
, LOCALEDIR
);
308 atexit(close_stdout
);
310 while ((c
= getopt_long(argc
, argv
, "Vh", longopts
, NULL
)) != -1)
313 printf(UTIL_LINUX_VERSION
);
321 /* check that sender has write enabled */
322 if (isatty(STDIN_FILENO
))
323 src_fd
= STDIN_FILENO
;
324 else if (isatty(STDOUT_FILENO
))
325 src_fd
= STDOUT_FILENO
;
326 else if (isatty(STDERR_FILENO
))
327 src_fd
= STDERR_FILENO
;
332 if (!(ctl
.src_tty
= ttyname(src_fd
)))
334 _("can't find your tty's name"));
337 * We may have /dev/ttyN but also /dev/pts/xx. Below,
338 * check_tty() will put "/dev/" in front, so remove that
341 if (!strncmp(ctl
.src_tty
, "/dev/", 5))
343 if (check_tty(ctl
.src_tty
, &tty_writeable
, NULL
, 1))
347 _("you have write permission turned off"));
350 ctl
.src_tty
= "<no tty>";
352 ctl
.src_uid
= getuid();
357 ctl
.dst_login
= argv
[1];
362 ctl
.dst_login
= argv
[1];
363 if (!strncmp(argv
[2], "/dev/", 5))
364 xstrncpy(ctl
.dst_tty
, argv
[2] + 5, sizeof(ctl
.dst_tty
));
366 xstrncpy(ctl
.dst_tty
, argv
[2], sizeof(ctl
.dst_tty
));
367 if (check_utmp(&ctl
))
369 _("%s is not logged in on %s"),
370 ctl
.dst_login
, ctl
.dst_tty
);
371 if (check_tty(ctl
.dst_tty
, &tty_writeable
, NULL
, 1))
373 if (ctl
.src_uid
&& !tty_writeable
)
375 _("%s has messages disabled on %s"),
376 ctl
.dst_login
, ctl
.dst_tty
);