1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include <pobl/bl_config.h>
4
5 #include "vt_pty_intern.h"
6
7 #include <pobl/bl_debug.h>
8 #include <pobl/bl_mem.h> /* realloc/alloca */
9 #include <pobl/bl_path.h>
10 #include <pobl/bl_str.h>
11 #include <pobl/bl_util.h> /* DIGIT_STR_LEN */
12 #include <string.h>
13 #include <unistd.h> /* ttyname/pipe */
14 #include <stdio.h> /* sscanf */
15 #include <fcntl.h> /* fcntl/O_BINARY */
16 #ifdef USE_WIN32API
17 #include <windows.h>
18 #else
19 #include <sys/stat.h>
20 #endif
21
22 #if 0
23 #define __DEBUG
24 #endif
25
26 /* --- global functions --- */
27
vt_pty_new(const char * cmd_path,char ** cmd_argv,char ** env,const char * host,const char * work_dir,const char * pass,const char * pubkey,const char * privkey,u_int cols,u_int rows,u_int width_pix,u_int height_pix)28 vt_pty_t *vt_pty_new(const char *cmd_path, /* can be NULL */
29 char **cmd_argv, /* can be NULL(only if cmd_path is NULL) */
30 char **env, /* can be NULL */
31 const char *host, /* DISPLAY env or remote host */
32 const char *work_dir, /* can be NULL */
33 const char *pass, /* can be NULL */
34 const char *pubkey, /* can be NULL */
35 const char *privkey, /* can be NULL */
36 u_int cols, u_int rows, u_int width_pix, u_int height_pix) {
37 vt_pty_t *pty;
38
39 #ifndef USE_WIN32API
40 if (!pass) {
41 pty =
42 vt_pty_unix_new(cmd_path, cmd_argv, env, host, work_dir, cols, rows, width_pix, height_pix);
43 } else
44 #endif
45 {
46 #if defined(USE_LIBSSH2)
47 if (strncmp(host, "mosh://", 7) == 0) {
48 pty = vt_pty_mosh_new(cmd_path, cmd_argv, env, host + 7, pass, pubkey, privkey, cols, rows,
49 width_pix, height_pix);
50 } else {
51 pty = vt_pty_ssh_new(cmd_path, cmd_argv, env, host, pass, pubkey, privkey, cols, rows,
52 width_pix, height_pix);
53 }
54 #elif defined(USE_WIN32API)
55 pty = vt_pty_pipe_new(cmd_path, cmd_argv, env, host, pass, cols, rows);
56 #else
57 pty = NULL;
58 #endif
59 }
60
61 if (pty) {
62 vt_config_menu_init(&pty->config_menu);
63 }
64
65 return pty;
66 }
67
vt_pty_new_with(int master,int slave,pid_t child_pid,u_int cols,u_int rows,u_int width_pix,u_int height_pix)68 vt_pty_t *vt_pty_new_with(int master, int slave, pid_t child_pid, u_int cols, u_int rows,
69 u_int width_pix, u_int height_pix) {
70 vt_pty_t *pty;
71
72 #ifndef USE_WIN32API
73 #if 1
74 struct stat st;
75 if (fstat(master, &st) == 0 && (st.st_mode & S_IFCHR))
76 #else
77 if (ptsname(master))
78 #endif
79 {
80 pty = vt_pty_unix_new_with(master, slave, child_pid, ":0.0", cols, rows, width_pix, height_pix);
81 } else
82 #endif
83 {
84 /* XXX vt_pty_ssh_new_with() and vt_pty_pipe_new_with() haven't been implemented yet. */
85 pty = NULL;
86 }
87
88 if (pty) {
89 vt_config_menu_init(&pty->config_menu);
90 }
91
92 return pty;
93 }
94
vt_pty_destroy(vt_pty_t * pty)95 int vt_pty_destroy(vt_pty_t *pty) {
96 #ifdef __DEBUG
97 bl_debug_printf(BL_DEBUG_TAG " vt_pty_destroy is called for %p.\n", pty);
98 #endif
99
100 if (pty->pty_listener && pty->pty_listener->closed) {
101 (*pty->pty_listener->closed)(pty->pty_listener->self);
102 }
103 #ifdef DEBUG
104 else {
105 bl_debug_printf(BL_DEBUG_TAG " %s is not set.\n",
106 pty->pty_listener ? "pty_listener->closed" : "pty listener");
107 }
108 #endif
109
110 free(pty->buf);
111 free(pty->cmd_line);
112 vt_config_menu_final(&pty->config_menu);
113
114 (*pty->final)(pty);
115
116 free(pty);
117
118 return 1;
119 }
120
vt_set_pty_winsize(vt_pty_t * pty,u_int cols,u_int rows,u_int width_pix,u_int height_pix)121 int vt_set_pty_winsize(vt_pty_t *pty, u_int cols, u_int rows, u_int width_pix, u_int height_pix) {
122 return (*pty->set_winsize)(pty, cols, rows, width_pix, height_pix);
123 }
124
125 /*
126 * Return size of lost bytes.
127 */
vt_write_to_pty(vt_pty_t * pty,u_char * buf,size_t len)128 size_t vt_write_to_pty(vt_pty_t *pty, u_char *buf, size_t len /* if 0, flushing buffer. */
129 ) {
130 u_char *w_buf;
131 size_t w_buf_size;
132 ssize_t written_size;
133 void *p;
134
135 w_buf_size = pty->left + len;
136 if (w_buf_size == 0) {
137 return 0;
138 }
139 #if 0
140 /*
141 * Little influence without this buffering.
142 */
143 else if (len > 0 && w_buf_size < 16) {
144 /*
145 * Buffering until 16 bytes.
146 */
147
148 if (pty->size < 16) {
149 if ((p = realloc(pty->buf, 16)) == NULL) {
150 #ifdef DEBUG
151 bl_warn_printf(BL_DEBUG_TAG " realloc failed. %d characters not written.\n", len);
152 #endif
153
154 return len;
155 }
156
157 pty->size = 16;
158 pty->buf = p;
159 }
160
161 memcpy(&pty->buf[pty->left], buf, len);
162 pty->left = w_buf_size;
163
164 #if 0
165 bl_debug_printf("buffered(not written) %d characters.\n", pty->left);
166 #endif
167
168 return 0;
169 }
170 #endif
171
172 if (/* pty->buf && */ len == 0) {
173 w_buf = pty->buf;
174 } else if (/* pty->buf == NULL && */ pty->left == 0) {
175 w_buf = buf;
176 } else if ((w_buf = alloca(w_buf_size))) {
177 memcpy(w_buf, pty->buf, pty->left);
178 memcpy(&w_buf[pty->left], buf, len);
179 } else {
180 #ifdef DEBUG
181 bl_warn_printf(BL_DEBUG_TAG " alloca() failed. %d characters not written.\n", len);
182 #endif
183
184 return len;
185 }
186
187 #ifdef __DEBUG
188 {
189 int i;
190 for (i = 0; i < w_buf_size; i++) {
191 bl_msg_printf("%.2x", w_buf[i]);
192 }
193 bl_msg_printf("\n");
194 }
195 #endif
196
197 written_size = (*pty->write)(pty, w_buf, w_buf_size);
198
199 if (written_size < 0) {
200 #ifdef DEBUG
201 bl_warn_printf(BL_DEBUG_TAG " write() failed.\n");
202 #endif
203 written_size = 0;
204 }
205
206 if (written_size == w_buf_size) {
207 pty->left = 0;
208
209 return 0;
210 }
211
212 /* w_buf_size - written_size == not_written_size */
213 if (w_buf_size - written_size > pty->size) {
214 if ((p = realloc(pty->buf, w_buf_size - written_size)) == NULL) {
215 size_t lost;
216
217 if (pty->size == 0) {
218 lost = w_buf_size - written_size;
219 pty->left = 0;
220 } else {
221 lost = w_buf_size - written_size - pty->size;
222 memcpy(pty->buf, &w_buf[written_size], pty->size);
223 pty->left = pty->size;
224 }
225
226 #ifdef DEBUG
227 bl_warn_printf(BL_DEBUG_TAG " realloc failed. %d characters are not written.\n", lost);
228 #endif
229
230 return lost;
231 } else {
232 pty->size = pty->left = w_buf_size - written_size;
233 pty->buf = p;
234 }
235 } else {
236 pty->left = w_buf_size - written_size;
237 }
238
239 memcpy(pty->buf, &w_buf[written_size], pty->left);
240
241 #if 0
242 bl_debug_printf("%d is not written.\n", pty->left);
243 #endif
244
245 return 0;
246 }
247
vt_read_pty(vt_pty_t * pty,u_char * buf,size_t left)248 size_t vt_read_pty(vt_pty_t *pty, u_char *buf, size_t left) {
249 size_t read_size;
250
251 read_size = 0;
252 while (1) {
253 ssize_t ret;
254
255 ret = (*pty->read)(pty, &buf[read_size], left);
256 if (ret <= 0) {
257 return read_size;
258 } else {
259 read_size += ret;
260 left -= ret;
261 }
262 }
263 }
264
vt_response_config(vt_pty_t * pty,char * key,char * value,int to_menu)265 void vt_response_config(vt_pty_t *pty, char *key, char *value, int to_menu) {
266 char *res;
267 char *fmt;
268 size_t res_len;
269
270 res_len = 1 + strlen(key) + 1;
271 if (value) {
272 res_len += (1 + strlen(value));
273 fmt = "#%s=%s\n";
274 } else {
275 fmt = "#%s\n";
276 }
277
278 if (!(res = alloca(res_len + 1))) {
279 res = "#error\n";
280 }
281
282 sprintf(res, fmt, key, value);
283
284 #ifdef __DEBUG
285 bl_debug_printf(BL_DEBUG_TAG " %s\n", res);
286 #endif
287
288 if (to_menu < 0) {
289 if (pty->pty_listener && pty->pty_listener->show_config) {
290 /* '\n' -> '\0' */
291 res[strlen(res) - 1] = '\0';
292 (*pty->pty_listener->show_config)(pty->pty_listener->self, res + 1);
293 }
294 } else if (to_menu > 0) {
295 vt_config_menu_write(&pty->config_menu, res, res_len);
296 } else {
297 vt_write_to_pty(pty, res, res_len);
298 }
299 }
300
301 /*
302 * Always return non-NULL value.
303 * XXX Static data can be returned. (Not reentrant)
304 */
vt_pty_get_slave_name(vt_pty_t * pty)305 char *vt_pty_get_slave_name(vt_pty_t *pty) {
306 static char virt_name[9 + DIGIT_STR_LEN(int)+1];
307 #ifndef USE_WIN32API
308 char *name;
309
310 if (pty->slave >= 0 && (name = ttyname(pty->slave))) {
311 return name;
312 }
313 #endif
314
315 /* Virtual pty name */
316 #ifdef USE_LIBSSH2
317 sprintf(virt_name, "/dev/vpty%d", ((pty->child_pid >> 1) & 0xfff)); /* child_pid == channel */
318 #else
319 sprintf(virt_name, "/dev/vpty%d", pty->master);
320 #endif
321
322 return virt_name;
323 }
324
vt_start_config_menu(vt_pty_t * pty,char * cmd_path,int x,int y,char * display)325 int vt_start_config_menu(vt_pty_t *pty, char *cmd_path, int x, int y, char *display) {
326 return vt_config_menu_start(&pty->config_menu, cmd_path, x, y, display, pty);
327 }
328