]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/write.c
textual: use manual tail usage() macro
[thirdparty/util-linux.git] / term-utils / write.c
CommitLineData
6dbe3af9 1/*
726f69e2
KZ
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
6dbe3af9
KZ
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
23 *
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
34 * SUCH DAMAGE.
35 *
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)
fd6b7a7f
KZ
39 * Mon Jul 1 17:01:39 MET DST 1996, janl@math.uio.no:
40 * - Added fix from David.Chapell@mail.trincoll.edu enabeling daemons
41 * to use write.
42 * - ANSIed it since I was working on it anyway.
b50945d4 43 * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
7eda085c
KZ
44 * - added Native Language Support
45 *
6dbe3af9
KZ
46 */
47
22853e4a 48#include <stdio.h>
fd6b7a7f 49#include <unistd.h>
6dbe3af9 50#include <utmp.h>
22853e4a 51#include <errno.h>
22853e4a 52#include <time.h>
6dbe3af9 53#include <pwd.h>
6dbe3af9 54#include <string.h>
66ee8158 55#include <stdlib.h>
22853e4a 56#include <signal.h>
fd6b7a7f 57#include <sys/param.h>
fd6b7a7f 58#include <sys/stat.h>
6dbe3af9 59#include <paths.h>
aa44b95f 60#include <getopt.h>
cdd2a8c3 61
5f51b8b2 62#include "c.h"
66ee8158 63#include "carefulputc.h"
cdd2a8c3 64#include "closestream.h"
7eda085c 65#include "nls.h"
a0fc344b 66#include "xalloc.h"
5f51b8b2
SK
67
68static void __attribute__ ((__noreturn__)) usage(FILE * out);
fd6b7a7f
KZ
69void search_utmp(char *, char *, char *, uid_t);
70void do_write(char *, char *, uid_t);
71void wr_fputs(char *);
5f51b8b2 72static void __attribute__ ((__noreturn__)) done(int);
fd6b7a7f
KZ
73int term_chk(char *, int *, time_t *, int);
74int utmp_chk(char *, char *);
75
0233a8ea 76static gid_t root_access;
bf09b61a 77
aa44b95f
SK
78static void __attribute__ ((__noreturn__)) usage(FILE * out)
79{
4c27cd90
KZ
80 fputs(_("\nUsage:\n"), out);
81 fprintf(out,
82 _(" %s [options] <user> [<ttyname>]\n"),
83 program_invocation_short_name);
aa44b95f 84
4c27cd90
KZ
85 fputs(_("\nOptions:\n"), out);
86 fputs(_(" -V, --version output version information and exit\n"
87 " -h, --help display this help and exit\n\n"), out);
aa44b95f 88
a587cc55 89 fprintf(out, USAGE_MAN_TAIL("write(1)"));
aa44b95f
SK
90 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
91}
92
5f51b8b2
SK
93int main(int argc, char **argv)
94{
6dbe3af9
KZ
95 time_t atime;
96 uid_t myuid;
aa44b95f 97 int msgsok, myttyfd, c;
e78a7ecd 98 char tty[PATH_MAX], *mytty;
6dbe3af9 99
aa44b95f
SK
100 static const struct option longopts[] = {
101 {"version", no_argument, NULL, 'V'},
102 {"help", no_argument, NULL, 'h'},
103 {NULL, 0, NULL, 0}
104 };
105
7eda085c
KZ
106 setlocale(LC_ALL, "");
107 bindtextdomain(PACKAGE, LOCALEDIR);
108 textdomain(PACKAGE);
cdd2a8c3 109 atexit(close_stdout);
726f69e2 110
aa44b95f
SK
111 while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
112 switch (c) {
113 case 'V':
f6277500 114 printf(UTIL_LINUX_VERSION);
aa44b95f
SK
115 return EXIT_SUCCESS;
116 case 'h':
117 usage(stdout);
118 default:
119 usage(stderr);
120 }
121
0233a8ea 122 root_access = !getegid();
bf09b61a 123
6dbe3af9
KZ
124 /* check that sender has write enabled */
125 if (isatty(fileno(stdin)))
126 myttyfd = fileno(stdin);
127 else if (isatty(fileno(stdout)))
128 myttyfd = fileno(stdout);
129 else if (isatty(fileno(stderr)))
130 myttyfd = fileno(stderr);
5f51b8b2
SK
131 else
132 myttyfd = -1;
133
fd6b7a7f 134 if (myttyfd != -1) {
5f51b8b2
SK
135 if (!(mytty = ttyname(myttyfd)))
136 errx(EXIT_FAILURE,
137 _("can't find your tty's name"));
138
139 /*
140 * We may have /dev/ttyN but also /dev/pts/xx. Below,
141 * term_chk() will put "/dev/" in front, so remove that
142 * part.
143 */
144 if (!strncmp(mytty, "/dev/", 5))
145 mytty += 5;
146 if (term_chk(mytty, &msgsok, &atime, 1))
147 exit(EXIT_FAILURE);
148 if (!msgsok)
149 errx(EXIT_FAILURE,
1bf3e8a8 150 _("you have write permission turned off"));
5f51b8b2
SK
151
152 } else
153 mytty = "<no tty>";
6dbe3af9
KZ
154
155 myuid = getuid();
156
157 /* check args */
158 switch (argc) {
159 case 2:
160 search_utmp(argv[1], tty, mytty, myuid);
161 do_write(tty, mytty, myuid);
162 break;
163 case 3:
164 if (!strncmp(argv[2], "/dev/", 5))
165 argv[2] += 5;
5f51b8b2
SK
166 if (utmp_chk(argv[1], argv[2]))
167 errx(EXIT_FAILURE,
1bf3e8a8 168 _("%s is not logged in on %s"),
5f51b8b2 169 argv[1], argv[2]);
6dbe3af9 170 if (term_chk(argv[2], &msgsok, &atime, 1))
5f51b8b2
SK
171 exit(EXIT_FAILURE);
172 if (myuid && !msgsok)
173 errx(EXIT_FAILURE,
174 _("%s has messages disabled on %s"),
175 argv[1], argv[2]);
6dbe3af9
KZ
176 do_write(argv[2], mytty, myuid);
177 break;
178 default:
aa44b95f 179 usage(stderr);
6dbe3af9 180 }
5f51b8b2 181
22853e4a 182 done(0);
6dbe3af9 183 /* NOTREACHED */
5f51b8b2 184 return EXIT_FAILURE;
6dbe3af9
KZ
185}
186
fd6b7a7f 187
6dbe3af9
KZ
188/*
189 * utmp_chk - checks that the given user is actually logged in on
190 * the given tty
191 */
fd6b7a7f 192int utmp_chk(char *user, char *tty)
6dbe3af9
KZ
193{
194 struct utmp u;
fd6b7a7f
KZ
195 struct utmp *uptr;
196 int res = 1;
6dbe3af9 197
fd6b7a7f
KZ
198 utmpname(_PATH_UTMP);
199 setutent();
6dbe3af9 200
fd6b7a7f
KZ
201 while ((uptr = getutent())) {
202 memcpy(&u, uptr, sizeof(u));
cfa7fe89 203 if (strncmp(user, u.ut_user, sizeof(u.ut_user)) == 0 &&
6dbe3af9 204 strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
fd6b7a7f
KZ
205 res = 0;
206 break;
6dbe3af9 207 }
fd6b7a7f 208 }
6dbe3af9 209
fd6b7a7f 210 endutent();
5f51b8b2 211 return res;
6dbe3af9
KZ
212}
213
214/*
215 * search_utmp - search utmp for the "best" terminal to write to
216 *
217 * Ignores terminals with messages disabled, and of the rest, returns
218 * the one with the most recent access time. Returns as value the number
219 * of the user's terminals with messages enabled, or -1 if the user is
220 * not logged in at all.
221 *
222 * Special case for writing to yourself - ignore the terminal you're
223 * writing from, unless that's the only terminal with messages enabled.
224 */
fd6b7a7f 225void search_utmp(char *user, char *tty, char *mytty, uid_t myuid)
6dbe3af9
KZ
226{
227 struct utmp u;
fd6b7a7f 228 struct utmp *uptr;
6dbe3af9 229 time_t bestatime, atime;
fd6b7a7f 230 int nloggedttys, nttys, msgsok, user_is_me;
6dbe3af9 231 char atty[sizeof(u.ut_line) + 1];
6dbe3af9 232
fd6b7a7f
KZ
233 utmpname(_PATH_UTMP);
234 setutent();
6dbe3af9
KZ
235
236 nloggedttys = nttys = 0;
237 bestatime = 0;
238 user_is_me = 0;
fd6b7a7f
KZ
239 while ((uptr = getutent())) {
240 memcpy(&u, uptr, sizeof(u));
cfa7fe89 241 if (strncmp(user, u.ut_user, sizeof(u.ut_user)) == 0) {
6dbe3af9 242 ++nloggedttys;
5f51b8b2 243 strncpy(atty, u.ut_line, sizeof(u.ut_line));
6dbe3af9 244 atty[sizeof(u.ut_line)] = '\0';
6dbe3af9 245 if (term_chk(atty, &msgsok, &atime, 0))
5f51b8b2
SK
246 /* bad term? skip */
247 continue;
6dbe3af9 248 if (myuid && !msgsok)
5f51b8b2
SK
249 /* skip ttys with msgs off */
250 continue;
6dbe3af9
KZ
251 if (strcmp(atty, mytty) == 0) {
252 user_is_me = 1;
5f51b8b2
SK
253 /* don't write to yourself */
254 continue;
6dbe3af9 255 }
5f51b8b2
SK
256 if (u.ut_type != USER_PROCESS)
257 /* it's not a valid entry */
258 continue;
6dbe3af9
KZ
259 ++nttys;
260 if (atime > bestatime) {
261 bestatime = atime;
5f51b8b2 262 strcpy(tty, atty);
6dbe3af9
KZ
263 }
264 }
fd6b7a7f 265 }
6dbe3af9 266
fd6b7a7f 267 endutent();
5f51b8b2
SK
268 if (nloggedttys == 0)
269 errx(EXIT_FAILURE, _("%s is not logged in"), user);
6dbe3af9 270 if (nttys == 0) {
5f51b8b2
SK
271 if (user_is_me) {
272 /* ok, so write to yourself! */
273 strcpy(tty, mytty);
6dbe3af9
KZ
274 return;
275 }
5f51b8b2 276 errx(EXIT_FAILURE, _("%s has messages disabled"), user);
6dbe3af9 277 } else if (nttys > 1) {
5f51b8b2
SK
278 warnx(_("%s is logged in more than once; writing to %s"),
279 user, tty);
6dbe3af9
KZ
280 }
281}
282
283/*
284 * term_chk - check that a terminal exists, and get the message bit
285 * and the access time
286 */
5f51b8b2 287int term_chk(char *tty, int *msgsokP, time_t * atimeP, int showerror)
6dbe3af9
KZ
288{
289 struct stat s;
e78a7ecd 290 char path[PATH_MAX];
6dbe3af9 291
2b6fc908 292 if (strlen(tty) + 6 > sizeof(path))
5f51b8b2
SK
293 return 1;
294 sprintf(path, "/dev/%s", tty);
6dbe3af9
KZ
295 if (stat(path, &s) < 0) {
296 if (showerror)
5f51b8b2
SK
297 warn("%s", path);
298 return 1;
6dbe3af9 299 }
bf09b61a 300
0233a8ea
SK
301 *msgsokP = !access(path, W_OK);
302 if (!root_access && *msgsokP)
303 *msgsokP = s.st_mode & S_IWGRP;
6dbe3af9 304 *atimeP = s.st_atime;
5f51b8b2 305 return 0;
6dbe3af9
KZ
306}
307
308/*
309 * do_write - actually make the connection
310 */
5f51b8b2
SK
311void do_write(char *tty, char *mytty, uid_t myuid)
312{
66ee8158
KZ
313 char *login, *pwuid, *nows;
314 struct passwd *pwd;
22853e4a 315 time_t now;
a0fc344b 316 char path[PATH_MAX], *host, line[512];
6dbe3af9 317
66ee8158
KZ
318 /* Determine our login name(s) before the we reopen() stdout */
319 if ((pwd = getpwuid(myuid)) != NULL)
320 pwuid = pwd->pw_name;
321 else
322 pwuid = "???";
323 if ((login = getlogin()) == NULL)
324 login = pwuid;
6dbe3af9 325
2b6fc908 326 if (strlen(tty) + 6 > sizeof(path))
5f51b8b2 327 errx(EXIT_FAILURE, _("tty path %s too long"), tty);
b89fdd9c 328 snprintf(path, sizeof(path), "/dev/%s", tty);
5f51b8b2
SK
329 if ((freopen(path, "w", stdout)) == NULL)
330 err(EXIT_FAILURE, "%s", path);
6dbe3af9 331
5f51b8b2
SK
332 signal(SIGINT, done);
333 signal(SIGHUP, done);
6dbe3af9
KZ
334
335 /* print greeting */
a0fc344b
KZ
336 host = xgethostname();
337 if (!host)
338 host = xstrdup("???");
339
5f51b8b2 340 now = time((time_t *) NULL);
6dbe3af9
KZ
341 nows = ctime(&now);
342 nows[16] = '\0';
7eda085c 343 printf("\r\n\007\007\007");
66ee8158 344 if (strcmp(login, pwuid))
5f51b8b2
SK
345 printf(_("Message from %s@%s (as %s) on %s at %s ..."),
346 login, host, pwuid, mytty, nows + 11);
66ee8158 347 else
5f51b8b2
SK
348 printf(_("Message from %s@%s on %s at %s ..."),
349 login, host, mytty, nows + 11);
a0fc344b 350 free(host);
7eda085c 351 printf("\r\n");
6dbe3af9
KZ
352
353 while (fgets(line, sizeof(line), stdin) != NULL)
354 wr_fputs(line);
355}
356
357/*
358 * done - cleanup and exit
359 */
5f51b8b2
SK
360static void __attribute__ ((__noreturn__))
361 done(int dummy __attribute__ ((__unused__)))
362{
363 printf("EOF\r\n");
364 _exit(EXIT_SUCCESS);
6dbe3af9
KZ
365}
366
367/*
368 * wr_fputs - like fputs(), but makes control characters visible and
66ee8158 369 * turns \n into \r\n.
6dbe3af9 370 */
5f51b8b2
SK
371void wr_fputs(char *s)
372{
fd6b7a7f 373 char c;
6dbe3af9 374
78a3b0af 375#define PUTC(c) if (fputc_careful(c, stdout, '^') == EOF) \
5f51b8b2
SK
376 err(EXIT_FAILURE, _("carefulputc failed"));
377 while (*s) {
66ee8158
KZ
378 c = *s++;
379 if (c == '\n')
6dbe3af9 380 PUTC('\r');
66ee8158 381 PUTC(c);
6dbe3af9
KZ
382 }
383 return;
6dbe3af9
KZ
384#undef PUTC
385}