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
56 #include <sys/param.h>
63 #include "carefulputc.h"
64 #include "closestream.h"
70 static sig_atomic_t signal_received
= 0;
72 struct write_control
{
74 const char *src_login
;
75 const char *src_tty_path
;
76 const char *src_tty_name
;
77 const char *dst_login
;
79 const char *dst_tty_name
;
82 static void __attribute__((__noreturn__
)) usage(void)
85 fputs(USAGE_HEADER
, out
);
87 _(" %s [options] <user> [<ttyname>]\n"),
88 program_invocation_short_name
);
90 fputs(USAGE_SEPARATOR
, out
);
91 fputs(_("Send a message to another user.\n"), out
);
93 fputs(USAGE_OPTIONS
, out
);
94 printf(USAGE_HELP_OPTIONS(16));
95 printf(USAGE_MAN_TAIL("write(1)"));
100 * check_tty - check that a terminal exists, and get the message bit
101 * and the access time
103 static int check_tty(const char *tty
, int *tty_writeable
, time_t *tty_atime
, int showerror
)
107 if (stat(tty
, &s
) < 0) {
112 if (getuid() == 0) /* root can always write */
115 if (getegid() != s
.st_gid
) {
116 warnx(_("effective gid does not match group of %s"), tty
);
119 *tty_writeable
= s
.st_mode
& S_IWGRP
;
122 *tty_atime
= s
.st_atime
;
127 * check_utmp - checks that the given user is actually logged in on
130 static int check_utmp(const struct write_control
*ctl
)
135 utmpxname(_PATH_UTMP
);
138 while ((u
= getutxent())) {
139 if (strncmp(ctl
->dst_login
, u
->ut_user
, sizeof(u
->ut_user
)) == 0 &&
140 strncmp(ctl
->dst_tty_name
, u
->ut_line
, sizeof(u
->ut_line
)) == 0) {
151 * search_utmp - search utmp for the "best" terminal to write to
153 * Ignores terminals with messages disabled, and of the rest, returns
154 * the one with the most recent access time. Returns as value the number
155 * of the user's terminals with messages enabled, or -1 if the user is
156 * not logged in at all.
158 * Special case for writing to yourself - ignore the terminal you're
159 * writing from, unless that's the only terminal with messages enabled.
161 static void search_utmp(struct write_control
*ctl
)
164 time_t best_atime
= 0, tty_atime
;
165 int num_ttys
= 0, valid_ttys
= 0, tty_writeable
= 0, user_is_me
= 0;
166 char path
[sizeof(u
->ut_line
) + 6];
168 utmpxname(_PATH_UTMP
);
171 while ((u
= getutxent())) {
172 if (strncmp(ctl
->dst_login
, u
->ut_user
, sizeof(u
->ut_user
)) != 0)
175 sprintf(path
, "/dev/%s", u
->ut_line
);
176 if (check_tty(path
, &tty_writeable
, &tty_atime
, 0))
179 if (ctl
->src_uid
&& !tty_writeable
)
180 /* skip ttys with msgs off */
182 if (strcmp(u
->ut_line
, ctl
->src_tty_name
) == 0) {
184 /* don't write to yourself */
187 if (u
->ut_type
!= USER_PROCESS
)
188 /* it's not a valid entry */
191 if (best_atime
< tty_atime
) {
192 best_atime
= tty_atime
;
193 free(ctl
->dst_tty_path
);
194 ctl
->dst_tty_path
= xstrdup(path
);
195 ctl
->dst_tty_name
= ctl
->dst_tty_path
+ 5;
201 errx(EXIT_FAILURE
, _("%s is not logged in"), ctl
->dst_login
);
202 if (valid_ttys
== 0) {
204 /* ok, so write to yourself! */
205 if (!ctl
->src_tty_path
)
206 errx(EXIT_FAILURE
, _("can't find your tty's name"));
207 ctl
->dst_tty_path
= xstrdup(ctl
->src_tty_path
);
208 ctl
->dst_tty_name
= ctl
->dst_tty_path
+ 5;
211 errx(EXIT_FAILURE
, _("%s has messages disabled"), ctl
->dst_login
);
214 warnx(_("%s is logged in more than once; writing to %s"),
215 ctl
->dst_login
, ctl
->dst_tty_name
);
219 * signal_handler - cause write loop to exit
221 static void signal_handler(int signo
)
223 signal_received
= signo
;
227 * write_line - like fputs(), but makes control characters visible and
228 * turns \n into \r\n.
230 static void write_line(char *s
)
235 if ((c
== '\n' && fputc_careful('\r', stdout
, '^') == EOF
)
236 || fputc_careful(c
, stdout
, '^') == EOF
)
237 err(EXIT_FAILURE
, _("carefulputc failed"));
242 * do_write - actually make the connection
244 static void do_write(const struct write_control
*ctl
)
250 char *host
, line
[512];
251 struct sigaction sigact
;
253 /* Determine our login name(s) before the we reopen() stdout */
254 if ((pwd
= getpwuid(ctl
->src_uid
)) != NULL
)
255 pwuid
= pwd
->pw_name
;
258 if ((login
= getlogin()) == NULL
)
261 if ((freopen(ctl
->dst_tty_path
, "w", stdout
)) == NULL
)
262 err(EXIT_FAILURE
, "%s", ctl
->dst_tty_path
);
264 sigact
.sa_handler
= signal_handler
;
265 sigemptyset(&sigact
.sa_mask
);
267 sigaction(SIGINT
, &sigact
, NULL
);
268 sigaction(SIGHUP
, &sigact
, NULL
);
270 host
= xgethostname();
272 host
= xstrdup("???");
274 now
= time((time_t *)NULL
);
275 tm
= localtime(&now
);
277 printf("\r\n\a\a\a");
278 if (strcmp(login
, pwuid
))
279 printf(_("Message from %s@%s (as %s) on %s at %02d:%02d ..."),
280 login
, host
, pwuid
, ctl
->src_tty_name
,
281 tm
->tm_hour
, tm
->tm_min
);
283 printf(_("Message from %s@%s on %s at %02d:%02d ..."),
284 login
, host
, ctl
->src_tty_name
,
285 tm
->tm_hour
, tm
->tm_min
);
289 while (fgets(line
, sizeof(line
), stdin
) != NULL
) {
297 int main(int argc
, char **argv
)
299 int tty_writeable
= 0, c
;
300 struct write_control ctl
= { 0 };
302 static const struct option longopts
[] = {
303 {"version", no_argument
, NULL
, 'V'},
304 {"help", no_argument
, NULL
, 'h'},
308 setlocale(LC_ALL
, "");
309 bindtextdomain(PACKAGE
, LOCALEDIR
);
311 close_stdout_atexit();
313 while ((c
= getopt_long(argc
, argv
, "Vh", longopts
, NULL
)) != -1)
316 print_version(EXIT_SUCCESS
);
320 errtryhelp(EXIT_FAILURE
);
323 if (get_terminal_name(&ctl
.src_tty_path
, &ctl
.src_tty_name
, NULL
) == 0) {
324 /* check that sender has write enabled */
325 if (check_tty(ctl
.src_tty_path
, &tty_writeable
, NULL
, 1))
329 _("you have write permission turned off"));
332 ctl
.src_tty_name
= "<no tty>";
334 ctl
.src_uid
= getuid();
339 ctl
.dst_login
= argv
[1];
344 ctl
.dst_login
= argv
[1];
345 if (!strncmp(argv
[2], "/dev/", 5))
346 ctl
.dst_tty_path
= xstrdup(argv
[2]);
348 xasprintf(&ctl
.dst_tty_path
, "/dev/%s", argv
[2]);
349 ctl
.dst_tty_name
= ctl
.dst_tty_path
+ 5;
350 if (check_utmp(&ctl
))
352 _("%s is not logged in on %s"),
353 ctl
.dst_login
, ctl
.dst_tty_name
);
354 if (check_tty(ctl
.dst_tty_path
, &tty_writeable
, NULL
, 1))
356 if (ctl
.src_uid
&& !tty_writeable
)
358 _("%s has messages disabled on %s"),
359 ctl
.dst_login
, ctl
.dst_tty_name
);
363 errtryhelp(EXIT_FAILURE
);
365 free(ctl
.dst_tty_path
);