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
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <sys/types.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <sys/ioctl.h>
34#include <sys/stat.h>
35#include <sys/time.h>
36#include <termios.h>
37#include <signal.h>
38#include <errno.h>
39
40#ifdef HAVE_WORKING_POLL
41#ifdef HAVE_POLL_H
42#include <poll.h>
43#else
44#include <sys/poll.h>
45#endif
46#undef HAVE_SELECT
47#endif
48
49#ifdef HAVE_SELECT
50#if !(defined(_MINIX) || defined(__BEOS__))
51#define HAVE_WORKING_SELECT 1
52#endif
53#endif
54
55#ifdef HAVE_WORKING_SELECT
56#if defined(HAVE_SYS_SELECT_H) && defined(HAVE_SYS_TIME_SELECT)
57#include <sys/select.h>
58#endif
59#endif
60
61#ifdef HAVE_PTY_H
62#include <pty.h>
63#endif
64
65#ifdef HAVE_STROPTS_H
66#include <stropts.h>
67#endif
68
69#ifdef HAVE_SYS_PARAM
70#include <sys/param.h>
71#endif
72
73#ifdef HAVE_OPENPTY
74#if defined(HAVE_UTIL_H)
75#include <util.h>
76#elif defined(HAVE_LIBUTIL_H)
77#include <libutil.h>
78#elif defined(HAVE_PTY_H)
79#include <pty.h>
80#endif
81#endif /* HAVE_OPENPTY */
82
83#include "sys.h"
84
85#ifdef USE_IGNORE_RC
86int ignore_unused;
87#endif
88
89#if defined(HAVE_OPENPTY)
90static int opened_tty = -1;
91#endif
92
93static int saved_tio_valid = 0;
94static struct termios saved_tio;
95
96int
97waitForOutput(int fd)
98{
99    int ret = 0;
100
101#if defined(HAVE_WORKING_POLL)
102    struct pollfd pfd[1];
103    int rc;
104
105    pfd[0].fd = fd;
106    pfd[0].events = POLLOUT;
107    pfd[0].revents = 0;
108
109    rc = poll(pfd, 1, -1);
110    if (rc < 0)
111	ret = -1;
112    else if (pfd[0].revents & (POLLOUT | POLLERR | POLLHUP))
113	ret = 1;
114
115#elif defined(HAVE_WORKING_SELECT)
116    fd_set fds;
117    int rc;
118
119    FD_ZERO(&fds);
120    FD_SET(fd, &fds);
121    rc = select(FD_SETSIZE, NULL, &fds, NULL, NULL);
122    if (rc < 0)
123	ret = -1;
124    else if (FD_ISSET(fd, &fds))
125	ret = 1;
126
127#else
128    ret = 1;
129#endif
130
131    return ret;
132}
133
134int
135waitForInput(int fd1, int fd2)
136{
137    int ret = 0;
138
139#if defined(HAVE_WORKING_POLL)
140    struct pollfd pfd[2];
141    int rc;
142
143    pfd[0].fd = fd1;
144    pfd[1].fd = fd2;
145    pfd[0].events = pfd[1].events = POLLIN;
146    pfd[0].revents = pfd[1].revents = 0;
147
148    rc = poll(pfd, 2, -1);
149    if (rc < 0) {
150	ret = -1;
151    } else {
152	if (pfd[0].revents & (POLLIN | POLLERR | POLLHUP))
153	    ret |= 1;
154	if (pfd[1].revents & (POLLIN | POLLERR | POLLHUP))
155	    ret |= 2;
156    }
157
158#elif defined(HAVE_WORKING_SELECT)
159    fd_set fds;
160    int rc;
161
162    FD_ZERO(&fds);
163    FD_SET(fd1, &fds);
164    FD_SET(fd2, &fds);
165    rc = select(FD_SETSIZE, &fds, NULL, NULL, NULL);
166    if (rc < 0) {
167	ret = -1;
168    } else {
169	if (FD_ISSET(fd1, &fds))
170	    ret |= 1;
171	if (FD_ISSET(fd2, &fds))
172	    ret |= 2;
173    }
174#else
175    ret = (1 | 2);
176#endif
177
178    return ret;
179}
180
181int
182setWindowSize(int sfd, int dfd)
183{
184#ifdef TIOCGWINSZ
185    int rc;
186    struct winsize ws;
187    rc = ioctl(sfd, TIOCGWINSZ, (char *) &ws);
188    if (rc < 0)
189	return -1;
190    rc = ioctl(dfd, TIOCSWINSZ, (char *) &ws);
191    if (rc < 0)
192	return -1;
193#endif
194    return 0;
195}
196
197int
198installHandler(int signum, void (*handler) (int))
199{
200    struct sigaction sa;
201    sigset_t ss;
202    int rc;
203
204    sigemptyset(&ss);
205
206    sa.sa_handler = handler;
207    sa.sa_mask = ss;
208    sa.sa_flags = 0;
209    rc = sigaction(signum, &sa, NULL);
210    return rc;
211}
212
213int
214copyTermios(int sfd, int dfd)
215{
216    struct termios tio;
217    int rc;
218
219    rc = tcgetattr(sfd, &tio);
220    if (rc < 0)
221	return -1;
222
223    rc = tcsetattr(dfd, TCSAFLUSH, &tio);
224    if (rc < 0)
225	return -1;
226
227    return 0;
228}
229
230int
231saveTermios(void)
232{
233    int rc;
234    rc = tcgetattr(0, &saved_tio);
235    if (rc >= 0)
236	saved_tio_valid = 1;
237    return rc;
238}
239
240int
241restoreTermios(void)
242{
243    if (!saved_tio_valid)
244	return -1;
245    return tcsetattr(0, TCSAFLUSH, &saved_tio);
246}
247
248int
249setRawTermios(void)
250{
251    struct termios tio;
252    int rc;
253
254    if (!saved_tio_valid)
255	saveTermios();
256    rc = tcgetattr(0, &tio);
257    if (rc < 0)
258	return rc;
259    tio.c_lflag &= (unsigned) ~(ECHO | ICANON | ISIG);
260    tio.c_iflag &= (unsigned) ~(ICRNL | IXOFF | IXON | ISTRIP);
261#ifdef ONLCR
262    tio.c_oflag &= (unsigned) ~ONLCR;
263#endif
264#ifdef OCRNL
265    tio.c_oflag &= (unsigned) ~OCRNL;
266#endif
267#ifdef ONOCR
268    tio.c_oflag &= (unsigned) ~ONOCR;
269#endif
270
271#ifdef VMIN
272    tio.c_cc[VMIN] = 0;
273    tio.c_cc[VTIME] = 0;
274#endif
275    rc = tcsetattr(0, TCSAFLUSH, &tio);
276    if (rc < 0)
277	return rc;
278    return 0;
279}
280
281char *
282my_basename(char *path)
283{
284    char *p;
285
286    p = strrchr(path, '/');
287    if (!p)
288	p = path;
289    else
290	p++;
291    return p;
292}
293
294static int
295fix_pty_perms(char *line)
296{
297    int rc;
298    struct stat s;
299
300    rc = stat(line, &s);
301    if (rc < 0)
302	return -1;
303    if (s.st_uid != getuid() || s.st_gid != getgid()) {
304	rc = chown(line, getuid(), getgid());
305	if (rc < 0) {
306	    fprintf(stderr,
307		    "Warning: could not change ownership of tty -- "
308		    "pty is insecure!\n");
309	    return 0;
310	}
311    }
312    if ((s.st_mode & 0777) != (S_IRUSR | S_IWUSR | S_IWGRP)) {
313	rc = chmod(line, S_IRUSR | S_IWUSR | S_IWGRP);
314	if (rc < 0) {
315	    fprintf(stderr,
316		    "Warning: could not change permissions of tty -- "
317		    "pty is insecure!\n");
318	    return 0;
319	}
320    }
321    return 1;
322}
323
324int
325allocatePty(int *pty_return, char **line_return)
326{
327    char name[12], *line = NULL;
328    int pty = -1;
329    const char *name1 = "pqrstuvwxyzPQRST";
330    char buffer[80];
331    char *name2 = strcpy(buffer, "0123456789abcdefghijklmnopqrstuv");
332    const char *p1;
333    char *p2;
334
335#if defined(HAVE_GRANTPT)
336    int rc;
337
338#ifdef HAVE_POSIX_OPENPT
339    pty = posix_openpt(O_RDWR);
340#else
341    pty = open("/dev/ptmx", O_RDWR);
342#endif
343    if (pty < 0)
344	goto bsd;
345
346    rc = grantpt(pty);
347    if (rc < 0) {
348	close(pty);
349	goto bsd;
350    }
351
352    rc = unlockpt(pty);
353    if (rc < 0) {
354	close(pty);
355	goto bsd;
356    }
357
358    line = strmalloc(ptsname(pty));
359    if (!line) {
360	close(pty);
361	goto bsd;
362    }
363
364    fix_pty_perms(line);
365
366    *pty_return = pty;
367    *line_return = line;
368    return 0;
369
370  bsd:
371#elif defined(HAVE_OPENPTY)
372    int rc;
373    char ttydev[80];		/* OpenBSD says at least 16 bytes */
374
375    rc = openpty(&pty, &opened_tty, ttydev, NULL, NULL);
376    if (rc < 0) {
377	close(pty);
378	goto bsd;
379    }
380    line = strmalloc(ttydev);
381    if (!line) {
382	close(pty);
383	goto bsd;
384    }
385
386    fix_pty_perms(line);
387
388    *pty_return = pty;
389    *line_return = line;
390    return 0;
391
392  bsd:
393#endif /* HAVE_GRANTPT, etc */
394
395    strcpy(name, "/dev/pty??");
396    for (p1 = name1; *p1; p1++) {
397	name[8] = *p1;
398	for (p2 = name2; *p2; p2++) {
399	    name[9] = *p2;
400	    pty = open(name, O_RDWR);
401	    if (pty >= 0)
402		goto found;
403	    /* Systems derived from 4.4BSD differ in their pty names,
404	       so ENOENT doesn't necessarily imply we're done. */
405	    continue;
406	}
407    }
408
409    goto bail;
410
411  found:
412    if ((line = strmalloc(name)) != 0) {
413	line[5] = 't';
414	fix_pty_perms(line);
415	*pty_return = pty;
416	*line_return = line;
417	return 0;
418    }
419
420  bail:
421    if (pty >= 0)
422	close(pty);
423    if (line)
424	free(line);
425    return -1;
426}
427
428int
429openTty(char *line)
430{
431    int rc;
432    int tty = -1;
433
434    tty = open(line, O_RDWR
435#if defined(TIOCSCTTY) && defined(O_NOCTTY)
436    /*
437     * Do not make this our controlling terminal, yet just in case it fails
438     * in some intermediate state.  But do not add this flag if we haven't
439     * the corresponding ioctl.
440     */
441	       | O_NOCTTY
442#endif
443	);
444
445    if (tty < 0)
446	goto bail;
447
448#if defined(HAVE_OPENPTY)
449    if (opened_tty >= 0) {
450	close(opened_tty);
451	opened_tty = -1;
452    }
453#endif
454
455#ifdef TIOCSCTTY
456    /*
457     * Now that we've successfully opened the terminal, make it the controlling
458     * terminal.  This call works only if the process does not already have a
459     * controlling terminal.
460     *
461     * Cygwin as of 2009/10/12 lacks this call, but has O_NOCTTY.
462     */
463    rc = ioctl(tty, TIOCSCTTY, (char *) 0);
464    if (rc < 0) {
465	goto bail;
466    }
467#endif
468
469#if defined(I_PUSH) && (defined(SVR4) || defined(__SVR4))
470    rc = ioctl(tty, I_PUSH, "ptem");
471    if (rc < 0)
472	goto bail;
473
474    rc = ioctl(tty, I_PUSH, "ldterm");
475    if (rc < 0)
476	goto bail;
477
478    rc = ioctl(tty, I_PUSH, "ttcompat");
479    if (rc < 0)
480	goto bail;
481#endif
482
483    return tty;
484
485  bail:
486    if (tty >= 0)
487	close(tty);
488    return -1;
489}
490
491/* Post-4.4 BSD systems have POSIX semantics (_POSIX_SAVED_IDS
492   or not, depending on the version).  4.3BSD and Minix do not have
493   saved IDs at all, so there's no issue. */
494int
495droppriv(void)
496{
497    int rc;
498#if defined(_POSIX_SAVED_IDS)
499    uid_t uid = getuid();
500    uid_t euid = geteuid();
501    gid_t gid = getgid();
502    gid_t egid = getegid();
503
504    if ((uid != euid || gid != egid) && euid != 0) {
505	errno = ENOSYS;
506	rc = -1;
507    } else {
508	rc = setuid(uid);
509	if (rc >= 0)
510	    rc = setgid(gid);
511    }
512#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(_MINIX)
513    rc = setuid(getuid());
514    if (rc >= 0) {
515	rc = setgid(getgid());
516    }
517#else
518    uid_t uid = getuid();
519    uid_t euid = geteuid();
520    gid_t gid = getgid();
521    gid_t egid = getegid();
522
523    if (uid != euid || gid != egid) {
524	errno = ENOSYS;
525	rc = -1;
526    } else {
527	rc = 0;
528    }
529#endif
530    return rc;
531}
532
533char *
534strmalloc(const char *value)
535{
536    char *result = 0;
537
538    if (value != 0) {
539#ifdef HAVE_STRDUP
540	result = strdup(value);
541#else
542	result = malloc(strlen(value) + 1);
543	if (result != 0)
544	    strcpy(result, value);
545#endif
546    }
547    return result;
548}
549
550#ifdef NO_LEAKS
551void
552ExitProgram(int code)
553{
554    luit_leaks();
555    iso2022_leaks();
556    charset_leaks();
557    exit(code);
558}
559#endif
560