e30f9e0eb81347456e6d90ac1b9ecb34a9881d9b
[u/mdw/putty] / contrib / cygtermd / pty.c
1 /*
2 * pty.c - pseudo-terminal handling
3 */
4
5 #define _XOPEN_SOURCE
6 #include <features.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <termios.h>
16 #include <sys/ioctl.h>
17 #include <sys/types.h>
18 #include <pwd.h>
19
20 #include "pty.h"
21 #include "malloc.h"
22
23 static char ptyname[FILENAME_MAX];
24 int master = -1;
25
26 void pty_preinit(void)
27 {
28 /*
29 * Allocate the pty.
30 */
31 master = open("/dev/ptmx", O_RDWR);
32 if (master < 0) {
33 perror("/dev/ptmx: open");
34 exit(1);
35 }
36
37 if (grantpt(master) < 0) {
38 perror("grantpt");
39 exit(1);
40 }
41
42 if (unlockpt(master) < 0) {
43 perror("unlockpt");
44 exit(1);
45 }
46 }
47
48 void pty_resize(int w, int h)
49 {
50 struct winsize sz;
51
52 assert(master >= 0);
53
54 sz.ws_row = h;
55 sz.ws_col = w;
56 sz.ws_xpixel = sz.ws_ypixel = 0;
57 ioctl(master, TIOCSWINSZ, &sz);
58 }
59
60 int run_program_in_pty(const struct shell_data *shdata,
61 char *directory, char **program_args)
62 {
63 int slave, pid;
64 char *fallback_args[2];
65
66 assert(master >= 0);
67
68 ptyname[FILENAME_MAX-1] = '\0';
69 strncpy(ptyname, ptsname(master), FILENAME_MAX-1);
70
71 #if 0
72 {
73 struct winsize ws;
74 struct termios ts;
75
76 /*
77 * FIXME: think up some good defaults here
78 */
79
80 if (!ioctl(0, TIOCGWINSZ, &ws))
81 ioctl(master, TIOCSWINSZ, &ws);
82 if (!tcgetattr(0, &ts))
83 tcsetattr(master, TCSANOW, &ts);
84 }
85 #endif
86
87 slave = open(ptyname, O_RDWR | O_NOCTTY);
88 if (slave < 0) {
89 perror("slave pty: open");
90 return 1;
91 }
92
93 /*
94 * Fork and execute the command.
95 */
96 pid = fork();
97 if (pid < 0) {
98 perror("fork");
99 return 1;
100 }
101
102 if (pid == 0) {
103 int i, fd;
104
105 /*
106 * We are the child.
107 */
108 close(master);
109
110 fcntl(slave, F_SETFD, 0); /* don't close on exec */
111 dup2(slave, 0);
112 dup2(slave, 1);
113 if (slave != 0 && slave != 1)
114 close(slave);
115 dup2(1, 2);
116 setsid();
117 setpgrp();
118 i = 0;
119 #ifdef TIOCNOTTY
120 if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
121 ioctl(fd, TIOCNOTTY, &i);
122 close(fd);
123 }
124 #endif
125 #ifdef TIOCSCTTY
126 ioctl(0, TIOCSCTTY, &i);
127 #endif
128 tcsetpgrp(0, getpgrp());
129
130 for (i = 0; i < shdata->nenvvars; i++)
131 putenv(shdata->envvars[i]);
132 if (shdata->termtype)
133 putenv(shdata->termtype);
134
135 if (directory)
136 chdir(directory);
137
138 /*
139 * Use the provided shell program name, if the user gave
140 * one. Failing that, use $SHELL; failing that, look up
141 * the user's default shell in the password file; failing
142 * _that_, revert to the bog-standard /bin/sh.
143 */
144 if (!program_args) {
145 char *shell;
146
147 shell = getenv("SHELL");
148 if (!shell) {
149 const char *login;
150 uid_t uid;
151 struct passwd *pwd;
152
153 /*
154 * For maximum generality in the face of multiple
155 * /etc/passwd entries with different login names and
156 * shells but a shared uid, we start by using
157 * getpwnam(getlogin()) if it's available - but we
158 * insist that its uid must match our real one, or we
159 * give up and fall back to getpwuid(getuid()).
160 */
161 uid = getuid();
162 login = getlogin();
163 if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid)
164 shell = pwd->pw_shell;
165 else if ((pwd = getpwuid(uid)))
166 shell = pwd->pw_shell;
167 }
168 if (!shell)
169 shell = "/bin/sh";
170
171 fallback_args[0] = shell;
172 fallback_args[1] = NULL;
173 program_args = fallback_args;
174 }
175
176 execv(program_args[0], program_args);
177
178 /*
179 * If we're here, exec has gone badly foom.
180 */
181 perror("exec");
182 exit(127);
183 }
184
185 close(slave);
186
187 return master;
188 }