sys.c revision dbe7da2e
1/*
2Copyright (c) 2001 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22/* $XFree86: xc/programs/luit/sys.c,v 1.9 2003/08/17 20:39:58 dawes Exp $ */
23
24#include <stdlib.h>
25#include <string.h>
26#include <stdio.h>
27#include <sys/types.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <sys/ioctl.h>
31#include <sys/stat.h>
32#include <sys/time.h>
33#include <termios.h>
34#include <signal.h>
35#include <errno.h>
36
37#ifdef HAVE_CONFIG_H
38# include "config.h"
39#endif
40
41#ifdef HAVE_POLL
42#include <sys/poll.h>
43#undef HAVE_SELECT
44#endif
45
46#ifdef HAVE_SYS_SELECT_H
47# include <sys/select.h>
48#endif
49
50#ifdef HAVE_PTY_H
51# include <pty.h>
52#endif
53
54#ifdef HAVE_STROPTS_H
55# include <stropts.h>
56#endif
57
58#ifdef HAVE_SYS_PARAM_H
59# include <sys/param.h>
60#endif
61
62#include "sys.h"
63
64static int saved_tio_valid = 0;
65static struct termios saved_tio;
66
67
68#ifdef HAVE_POLL
69int
70waitForOutput(int fd)
71{
72    struct pollfd pfd[1];
73    int rc;
74
75    pfd[0].fd = fd;
76    pfd[0].events = POLLOUT;
77    pfd[0].revents = 0;
78
79    rc = poll(pfd, 1, -1);
80    if(rc < 0)
81        return -1;
82
83    if(pfd[0].revents & POLLOUT)
84        return 1;
85
86    return 0;
87}
88
89int
90waitForInput(int fd1, int fd2)
91{
92    struct pollfd pfd[2];
93    int ret, rc;
94
95    pfd[0].fd = fd1;
96    pfd[1].fd = fd2;
97    pfd[0].events = pfd[1].events = POLLIN;
98    pfd[0].revents = pfd[1].revents = 0;
99
100    rc = poll(pfd, 2, -1);
101    if(rc < 0)
102        return -1;
103
104    ret = 0;
105    if(pfd[0].revents & (POLLIN | POLLERR | POLLHUP))
106        ret |= 1;
107    if(pfd[1].revents & (POLLIN | POLLERR | POLLHUP))
108        ret |= 2;
109    return ret;
110}
111#endif
112
113#ifdef HAVE_SELECT
114int
115waitForOutput(int fd)
116{
117    fd_set fds;
118    int rc;
119
120    FD_ZERO(&fds);
121    FD_SET(fd, &fds);
122    rc = select(FD_SETSIZE, NULL, &fds, NULL, NULL);
123    if(rc < 0)
124        return -1;
125
126    if(FD_ISSET(fd, &fds))
127        return 1;
128
129    return 0;
130}
131
132int
133waitForInput(int fd1, int fd2)
134{
135    fd_set fds;
136    int ret, rc;
137
138    FD_ZERO(&fds);
139    FD_SET(fd1, &fds);
140    FD_SET(fd2, &fds);
141    rc = select(FD_SETSIZE, &fds, NULL, NULL, NULL);
142    if(rc < 0)
143        return -1;
144
145    ret = 0;
146    if(FD_ISSET(fd1, &fds))
147        ret |= 1;
148    if(FD_ISSET(fd2, &fds))
149        ret |= 2;
150    return ret;
151}
152#endif
153
154#ifndef HAVE_POLL
155#ifndef HAVE_SELECT
156/* Busy looping implementation */
157int
158waitForOutput(int fd)
159{
160    return 1;
161}
162
163int
164waitForInput(int fd1, int fd2)
165{
166    return 1|2;
167}
168#endif
169#endif
170
171
172int
173setWindowSize(int sfd, int dfd)
174{
175#ifdef TIOCGWINSZ
176    int rc;
177    struct winsize ws;
178    rc = ioctl(sfd, TIOCGWINSZ, (char*)&ws);
179    if(rc < 0)
180        return -1;
181    rc = ioctl(dfd, TIOCSWINSZ, (char*)&ws);
182    if(rc < 0)
183        return -1;
184#endif
185    return 0;
186}
187
188int
189installHandler(int signum, void (*handler)(int))
190{
191    struct sigaction sa;
192    sigset_t ss;
193    int rc;
194
195    sigemptyset(&ss);
196
197    sa.sa_handler = handler;
198    sa.sa_mask = ss;
199    sa.sa_flags = 0;
200    rc = sigaction(signum, &sa, NULL);
201    return rc;
202}
203
204int
205copyTermios(int sfd, int dfd)
206{
207    struct termios tio;
208    int rc;
209
210    rc = tcgetattr(sfd, &tio);
211    if(rc < 0)
212        return -1;
213
214    rc = tcsetattr(dfd, TCSAFLUSH, &tio);
215    if(rc < 0)
216        return -1;
217
218    return 0;
219}
220
221int
222saveTermios(void)
223{
224    int rc;
225    rc = tcgetattr(0, &saved_tio);
226    if(rc >= 0)
227        saved_tio_valid = 1;
228    return rc;
229}
230
231int
232restoreTermios(void)
233{
234    if(!saved_tio_valid)
235        return -1;
236    return tcsetattr(0, TCSAFLUSH, &saved_tio);
237}
238
239int
240setRawTermios(void)
241{
242    struct termios tio;
243    int rc;
244
245    if(!saved_tio_valid)
246        saveTermios();
247    rc = tcgetattr(0, &tio);
248    if(rc < 0)
249        return rc;
250    tio.c_lflag &= ~(ECHO|ICANON|ISIG);
251    tio.c_iflag &= ~(ICRNL|IXOFF|IXON|ISTRIP);
252#ifdef ONLCR
253    tio.c_oflag &= ~ONLCR;
254#endif
255#ifdef OCRNL
256    tio.c_oflag &= ~OCRNL;
257#endif
258#ifdef ONOCR
259    tio.c_oflag &= ~ONOCR;
260#endif
261
262#ifdef VMIN
263    tio.c_cc[VMIN] = 0;
264    tio.c_cc[VTIME] = 0;
265#endif
266    rc = tcsetattr(0, TCSAFLUSH, &tio);
267    if(rc < 0)
268        return rc;
269    return 0;
270}
271
272
273char *
274my_basename(char *path)
275{
276    char *p;
277
278    p = strrchr(path, '/');
279    if(!p)
280        p = path;
281    else
282        p++;
283    return p;
284}
285
286static int
287fix_pty_perms(char *line)
288{
289    int rc;
290    struct stat s;
291    int uid = getuid(), gid = getgid();
292
293    rc = stat(line, &s);
294    if(rc < 0)
295        return -1;
296    if(s.st_uid != uid || s.st_gid != gid) {
297        rc = chown(line, getuid(), getgid());
298        if(rc < 0) {
299            fprintf(stderr,
300                    "Warning: could not change ownership of tty -- "
301                    "pty is insecure!\n");
302            return 0;
303        }
304    }
305    if((s.st_mode & 0777) != (S_IRUSR | S_IWUSR | S_IWGRP)) {
306        rc = chmod(line, S_IRUSR | S_IWUSR | S_IWGRP);
307        if (rc < 0) {
308            fprintf(stderr,
309                    "Warning: could not change permissions of tty -- "
310                    "pty is insecure!\n");
311            return 0;
312        }
313    }
314    return 1;
315}
316
317int
318allocatePty(int *pty_return, char **line_return)
319{
320    char name[12], *line = NULL;
321    int pty = -1;
322    char *name1 = "pqrstuvwxyzPQRST",
323        *name2 = "0123456789abcdefghijklmnopqrstuv";
324    char *p1, *p2;
325
326#ifdef HAVE_GRANTPT
327    char *temp_line;
328    int rc;
329
330#ifdef __APPLE__
331    pty = posix_openpt(O_RDWR);
332#else
333    pty = open("/dev/ptmx", O_RDWR);
334#endif
335
336    if(pty < 0)
337        goto bsd;
338
339    rc = grantpt(pty);
340    if(rc < 0) {
341        close(pty);
342        goto bsd;
343    }
344
345    rc = unlockpt(pty);
346    if(rc < 0) {
347        close(pty);
348        goto bsd;
349    }
350
351    temp_line = ptsname(pty);
352    if(!temp_line) {
353        close(pty);
354        goto bsd;
355    }
356    line = strdup(temp_line);
357    if(!line) {
358        close(pty);
359        return -1;
360    }
361
362    fix_pty_perms(line);
363
364    *pty_return = pty;
365    *line_return = line;
366    return 0;
367
368  bsd:
369#endif /* HAVE_GRANTPT */
370
371    strcpy(name, "/dev/pty??");
372    for(p1 = name1; *p1; p1++) {
373        name[8] = *p1;
374        for(p2 = name2; *p2; p2++) {
375            name[9] = *p2;
376            pty = open(name, O_RDWR);
377            if(pty >= 0)
378                goto found;
379            /* Systems derived from 4.4BSD differ in their pty names,
380               so ENOENT doesn't necessarily imply we're done. */
381            continue;
382        }
383    }
384
385    goto bail;
386
387  found:
388    line = strdup(name);
389    if(!line)
390	goto bail;
391    line[5] = 't';
392    fix_pty_perms(line);
393    *pty_return = pty;
394    *line_return = line;
395    return 0;
396
397  bail:
398    if(pty >= 0)
399        close(pty);
400    if(line)
401        free(line);
402    return -1;
403}
404
405int
406openTty(char *line)
407{
408    int rc;
409    int tty = -1;
410
411#if !defined(O_NOCTTY) || !defined(TIOCSCTTY)
412    /* e.g. Cygwin has a working O_NOCTTY but no TIOCSCTTY, so the tty
413       must be opened as controlling */
414    tty = open(line, O_RDWR);
415#else
416    /* The TIOCSCTTY ioctl below will fail if the process already has a
417       controlling tty (even if the current controlling tty is the same
418       as the tty you want to make controlling).  So we need to open
419       the tty with O_NOCTTY to make sure this doesn't happen. */
420    tty = open(line, O_RDWR | O_NOCTTY);
421#endif
422
423    if(tty < 0)
424        goto bail;
425
426#ifdef TIOCSCTTY
427    rc = ioctl(tty, TIOCSCTTY, (char *)0);
428    if(rc < 0) {
429        goto bail;
430    }
431#endif
432
433#if defined(SVR4) || defined(__SVR4)
434    rc = ioctl(tty, I_PUSH, "ptem");
435    if(rc < 0)
436        goto bail;
437
438    rc = ioctl(tty, I_PUSH, "ldterm");
439    if(rc < 0)
440        goto bail;
441
442    rc = ioctl(tty, I_PUSH, "ttcompat");
443    if(rc < 0)
444        goto bail;
445#endif
446
447    return tty;
448
449  bail:
450    if(tty >= 0)
451        close(tty);
452    return -1;
453}
454
455/* Post-4.4 BSD systems have POSIX semantics (_POSIX_SAVED_IDS
456   or not, depending on the version).  4.3BSD and Minix do not have
457   saved IDs at all, so there's no issue. */
458#if (defined(BSD) && !defined(_POSIX_SAVED_IDS)) || defined(_MINIX)
459int
460droppriv(void)
461{
462    int rc;
463    rc = setuid(getuid());
464    if(rc < 0)
465        return rc;
466    return setgid(getgid());
467}
468#elif defined(_POSIX_SAVED_IDS)
469int
470droppriv(void)
471{
472    int uid = getuid();
473    int euid = geteuid();
474    int gid = getgid();
475    int egid = getegid();
476    int rc;
477
478    if((uid != euid || gid != egid) && euid != 0) {
479        errno = ENOSYS;
480        return -1;
481    }
482    rc = setuid(uid);
483    if(rc < 0)
484        return rc;
485    return setgid(gid);
486}
487#else
488int
489droppriv(void)
490{
491    int uid = getuid();
492    int euid = geteuid();
493    int gid = getgid();
494    int egid = getegid();
495
496    if(uid != euid || gid != egid) {
497        errno = ENOSYS;
498        return -1;
499    }
500    return 0;
501}
502#endif
503