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