~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to term-utils/write.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

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