1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include "vt_pty_intern.h"
4
5 #include <stdio.h> /* sprintf */
6 #include <unistd.h> /* close */
7 #include <sys/ioctl.h>
8 #include <termios.h>
9 #include <signal.h> /* signal/SIGWINCH */
10 #include <string.h> /* strchr/memcpy */
11 #include <stdlib.h> /* putenv */
12 #include <pobl/bl_debug.h>
13 #include <pobl/bl_mem.h> /* realloc/alloca */
14 #include <pobl/bl_str.h> /* strdup */
15 #include <pobl/bl_pty.h>
16 #include <pobl/bl_path.h> /* bl_basename */
17 #include <pobl/bl_unistd.h> /* bl_killpg */
18 #ifdef USE_UTMP
19 #include <pobl/bl_utmp.h>
20 #endif
21 #ifdef __APPLE__
22 #include <errno.h>
23 #include <pobl/bl_sig_child.h>
24 #endif
25
26 #if 0
27 #define __DEBUG
28 #endif
29
30 typedef struct vt_pty_unix {
31 vt_pty_t pty;
32 #ifdef USE_UTMP
33 bl_utmp_t utmp;
34 #endif
35
36 } vt_pty_unix_t;
37
38 /* --- static functions --- */
39
final(vt_pty_t * pty)40 static int final(vt_pty_t *pty) {
41 #ifdef USE_UTMP
42 if (((vt_pty_unix_t*)pty)->utmp) {
43 bl_utmp_destroy(((vt_pty_unix_t*)pty)->utmp);
44 }
45 #endif
46
47 #ifdef __DEBUG
48 bl_debug_printf("PTY fd %d is closed\n", pty->master);
49 #endif
50
51 bl_pty_close(pty->master);
52 close(pty->slave);
53
54 return 1;
55 }
56
set_winsize(vt_pty_t * pty,u_int cols,u_int rows,u_int width_pix,u_int height_pix)57 static int set_winsize(vt_pty_t *pty, u_int cols, u_int rows, u_int width_pix, u_int height_pix) {
58 struct winsize ws;
59
60 #ifdef __DEBUG
61 bl_debug_printf(BL_DEBUG_TAG " win size cols %d rows %d width %d height %d.\n", cols, rows,
62 width_pix, height_pix);
63 #endif
64
65 ws.ws_col = cols;
66 ws.ws_row = rows;
67 ws.ws_xpixel = width_pix;
68 ws.ws_ypixel = height_pix;
69
70 if (ioctl(pty->master, TIOCSWINSZ, &ws) < 0) {
71 #ifdef DBEUG
72 bl_warn_printf(BL_DEBUG_TAG " ioctl(TIOCSWINSZ) failed.\n");
73 #endif
74
75 return 0;
76 }
77
78 if (pty->child_pid > 1) {
79 int pgrp;
80
81 #ifdef TIOCGPGRP
82 if (ioctl(pty->master, TIOCGPGRP, &pgrp) != -1) {
83 if (pgrp > 0) {
84 bl_killpg(pgrp, SIGWINCH);
85 }
86 } else
87 #endif
88 {
89 bl_killpg(pty->child_pid, SIGWINCH);
90 }
91 }
92
93 return 1;
94 }
95
write_to_pty(vt_pty_t * pty,u_char * buf,size_t len)96 static ssize_t write_to_pty(vt_pty_t *pty, u_char *buf, size_t len) {
97 #ifdef __APPLE__
98 ssize_t ret;
99
100 /*
101 * XXX
102 * If a child shell exits by 'exit' command, mlterm doesn't receive SIG_CHLD on
103 * iOS 4.3 with japanese software keyboard.
104 */
105 if ((ret = write(pty->master, buf, len)) < 0 && errno == EIO) {
106 bl_trigger_sig_child(pty->child_pid);
107 }
108
109 return ret;
110 #else
111 return write(pty->master, buf, len);
112 #endif
113 }
114
read_pty(vt_pty_t * pty,u_char * buf,size_t len)115 static ssize_t read_pty(vt_pty_t *pty, u_char *buf, size_t len) {
116 return read(pty->master, buf, len);
117 }
118
119 /* --- global functions --- */
120
vt_pty_unix_new(const char * cmd_path,char ** cmd_argv,char ** env,const char * host,const char * work_dir,u_int cols,u_int rows,u_int width_pix,u_int height_pix)121 vt_pty_t *vt_pty_unix_new(const char *cmd_path, /* If NULL, child prcess is not exec'ed. */
122 char **cmd_argv, /* can be NULL(only if cmd_path is NULL) */
123 char **env, /* can be NULL */
124 const char *host, const char *work_dir, u_int cols, u_int rows,
125 u_int width_pix, u_int height_pix) {
126 vt_pty_t *pty;
127 int master;
128 int slave;
129 pid_t pid;
130
131 pid = bl_pty_fork(&master, &slave);
132
133 if (pid == -1) {
134 return NULL;
135 }
136
137 if (pid == 0) {
138 /* child process */
139
140 if (work_dir) {
141 chdir(work_dir);
142 }
143
144 /* reset signals and spin off the command interpreter */
145 signal(SIGINT, SIG_DFL);
146 signal(SIGQUIT, SIG_DFL);
147 signal(SIGCHLD, SIG_DFL);
148 signal(SIGPIPE, SIG_DFL);
149
150 /*
151 * setting environmental variables.
152 */
153 if (env) {
154 while (*env) {
155 /*
156 * an argument string of putenv() must be allocated memory.
157 * (see SUSV2)
158 */
159 putenv(strdup(*env));
160
161 env++;
162 }
163 }
164
165 #if 0
166 /*
167 * XXX is this necessary ?
168 *
169 * mimick login's behavior by disabling the job control signals.
170 * a shell that wants them can turn them back on
171 */
172 signal(SIGTSTP, SIG_IGN);
173 signal(SIGTTIN, SIG_IGN);
174 signal(SIGTTOU, SIG_IGN);
175 #endif
176
177 if (!cmd_path) {
178 goto return_pty;
179 }
180
181 if (strchr(cmd_path, '/') == NULL) {
182 execvp(cmd_path, cmd_argv);
183 } else {
184 execv(cmd_path, cmd_argv);
185 }
186
187 #ifdef DEBUG
188 bl_warn_printf(BL_DEBUG_TAG " exec(%s) failed.\n", cmd_path);
189 #endif
190
191 exit(1);
192 }
193
194 /* parent process */
195
196 return_pty:
197 if (!(pty = vt_pty_unix_new_with(master, slave, pid, host, cols, rows, width_pix, height_pix))) {
198 close(master);
199 close(slave);
200 }
201
202 return pty;
203 }
204
vt_pty_unix_new_with(int master,int slave,pid_t child_pid,const char * host,u_int cols,u_int rows,u_int width_pix,u_int height_pix)205 vt_pty_t *vt_pty_unix_new_with(int master, int slave, pid_t child_pid, const char *host, u_int cols,
206 u_int rows, u_int width_pix, u_int height_pix) {
207 vt_pty_t *pty;
208
209 if ((pty = calloc(1, sizeof(vt_pty_unix_t))) == NULL) {
210 return NULL;
211 }
212
213 pty->final = final;
214 pty->set_winsize = set_winsize;
215 pty->write = write_to_pty;
216 pty->read = read_pty;
217 pty->master = master;
218 pty->slave = slave;
219 pty->mode = PTY_LOCAL;
220
221 if ((pty->child_pid = child_pid) > 0) {
222 /* Parent process */
223
224 #ifdef USE_UTMP
225 if ((((vt_pty_unix_t*)pty)->utmp =
226 bl_utmp_new(vt_pty_get_slave_name(pty), host, pty->master)) == NULL) {
227 #ifdef DEBUG
228 bl_warn_printf(BL_DEBUG_TAG "utmp failed.\n");
229 #endif
230 }
231 #endif
232
233 if (set_winsize(pty, cols, rows, width_pix, height_pix) == 0) {
234 #ifdef DEBUG
235 bl_warn_printf(BL_DEBUG_TAG " vt_set_pty_winsize() failed.\n");
236 #endif
237 }
238 }
239
240 return pty;
241 }
242