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