1616#include <sys/ioctl.h>
1717#include <termios.h>
1818
19- static char * *
20- serialize_string_tuple (PyObject * src ) {
21- Py_ssize_t sz = PyTuple_GET_SIZE (src );
19+ #define EXTRA_ENV_BUFFER_SIZE 64
2220
23- char * * ans = calloc (sz + 1 , sizeof (char * ));
24- if (!ans ) fatal ("Out of memory" );
21+ static char * *
22+ serialize_string_tuple (PyObject * src , Py_ssize_t extra ) {
23+ const Py_ssize_t sz = PyTuple_GET_SIZE (src );
24+ size_t required_size = sizeof (char * ) * (1 + sz + extra );
25+ required_size += extra * EXTRA_ENV_BUFFER_SIZE ;
26+ void * block = calloc (required_size , 1 );
27+ if (!block ) { PyErr_NoMemory (); return NULL ; }
28+ char * * ans = block ;
2529 for (Py_ssize_t i = 0 ; i < sz ; i ++ ) {
26- const char * pysrc = PyUnicode_AsUTF8 (PyTuple_GET_ITEM (src , i ));
27- if (!pysrc ) {
28- PyErr_Clear ();
29- RAII_PyObject (u8 , PyUnicode_AsEncodedString (PyTuple_GET_ITEM (src , i ), "UTF-8" , "ignore" ));
30- if (!u8 ) { PyErr_Print (); fatal ("couldn't parse command line" ); }
31- ans [i ] = calloc (PyBytes_GET_SIZE (u8 ) + 1 , sizeof (char ));
32- if (ans [i ] == NULL ) fatal ("Out of memory" );
33- memcpy (ans [i ], PyBytes_AS_STRING (u8 ), PyBytes_GET_SIZE (u8 ));
34- } else {
35- size_t len = strlen (pysrc );
36- ans [i ] = calloc (len + 1 , sizeof (char ));
37- if (ans [i ] == NULL ) fatal ("Out of memory" );
38- memcpy (ans [i ], pysrc , len );
39- }
30+ PyObject * x = PyTuple_GET_ITEM (src , i );
31+ if (!PyUnicode_Check (x )) { free (block ); PyErr_SetString (PyExc_TypeError , "string tuple must have only strings" ); return NULL ; }
32+ ans [i ] = (char * )PyUnicode_AsUTF8 (x );
33+ if (!ans [i ]) { free (block ); return NULL ; }
4034 }
4135 return ans ;
4236}
4337
44- static void
45- free_string_tuple (char * * data ) {
46- size_t i = 0 ;
47- while (data [i ]) free (data [i ++ ]);
48- free (data );
49- }
50-
51- extern char * * environ ;
52-
5338static void
5439write_to_stderr (const char * text ) {
5540 size_t sz = strlen (text );
@@ -86,8 +71,10 @@ spawn(PyObject *self UNUSED, PyObject *args) {
8671 if (!PyArg_ParseTuple (args , "ssO!O!iiiiiiO!spO!" , & exe , & cwd , & PyTuple_Type , & argv_p , & PyTuple_Type , & env_p , & master , & slave , & stdin_read_fd , & stdin_write_fd , & ready_read_fd , & ready_write_fd , & PyTuple_Type , & handled_signals_p , & kitten_exe , & forward_stdio , & PyTuple_Type , & pass_fds )) return NULL ;
8772 char name [2048 ] = {0 };
8873 if (ttyname_r (slave , name , sizeof (name ) - 1 ) != 0 ) { PyErr_SetFromErrno (PyExc_OSError ); return NULL ; }
89- char * * argv = serialize_string_tuple (argv_p );
90- char * * env = serialize_string_tuple (env_p );
74+ char * * argv = serialize_string_tuple (argv_p , 0 );
75+ if (!argv ) return NULL ;
76+ char * * env = serialize_string_tuple (env_p , 1 );
77+ if (!env ) { free (argv ); return NULL ; }
9178 int handled_signals [16 ] = {0 }, num_handled_signals = MIN ((int )arraysz (handled_signals ), PyTuple_GET_SIZE (handled_signals_p ));
9279 for (Py_ssize_t i = 0 ; i < num_handled_signals ; i ++ ) handled_signals [i ] = PyLong_AsLong (PyTuple_GET_ITEM (handled_signals_p , i ));
9380
@@ -129,23 +116,29 @@ spawn(PyObject *self UNUSED, PyObject *args) {
129116 if (ioctl (tfd , TIOCSCTTY , 0 ) == -1 ) exit_on_err ("Failed to set controlling terminal with TIOCSCTTY" );
130117 safe_close (tfd , __FILE__ , __LINE__ );
131118
132- int min_closed_fd = 3 ;
119+ fd_set passed_fds ; FD_ZERO (& passed_fds ); bool has_preserved_fds = false;
120+ if (forward_stdio ) {
121+ int fd = safe_dup (STDOUT_FILENO );
122+ if (fd < 0 ) exit_on_err ("dup() failed for forwarded STDOUT" );
123+ FD_SET (fd , & passed_fds );
124+ size_t s = PyTuple_GET_SIZE (env_p );
125+ env [s ] = (char * )(env + (s + 2 ));
126+ snprintf (env [s ], EXTRA_ENV_BUFFER_SIZE , "KITTY_STDIO_FORWARDED=%d" , fd );
127+ fd = safe_dup (STDERR_FILENO );
128+ if (fd < 0 ) exit_on_err ("dup() failed for forwarded STDERR" );
129+ FD_SET (fd , & passed_fds );
130+ has_preserved_fds = true;
131+ }
132+
133133 for (Py_ssize_t i = 0 ; i < PyTuple_GET_SIZE (pass_fds ); i ++ ) {
134134 PyObject * pfd = PyTuple_GET_ITEM (pass_fds , i );
135135 if (!PyLong_Check (pfd )) exit_on_err ("pass_fds must contain only integers" );
136136 int fd = PyLong_AsLong (pfd );
137- if (fd > -1 ) {
138- if (fd == min_closed_fd ) min_closed_fd ++ ;
139- else {
140- if (safe_dup2 (fd , min_closed_fd ++ ) == -1 ) exit_on_err ("dup2() failed for forwarded fd 1" );
141- safe_close (fd , __FILE__ , __LINE__ );
142- }
137+ if (fd > -1 && fd < FD_SETSIZE ) {
138+ FD_SET (fd , & passed_fds );
139+ has_preserved_fds = true;
143140 }
144141 }
145- if (forward_stdio ) {
146- if (safe_dup2 (STDOUT_FILENO , min_closed_fd ++ ) == -1 ) exit_on_err ("dup2() failed for forwarded fd 1" );
147- if (safe_dup2 (STDERR_FILENO , min_closed_fd ++ ) == -1 ) exit_on_err ("dup2() failed for forwarded fd 2" );
148- }
149142 // Redirect stdin/stdout/stderr to the pty
150143 if (safe_dup2 (slave , STDOUT_FILENO ) == -1 ) exit_on_err ("dup2() failed for fd number 1" );
151144 if (safe_dup2 (slave , STDERR_FILENO ) == -1 ) exit_on_err ("dup2() failed for fd number 2" );
@@ -165,10 +158,10 @@ spawn(PyObject *self UNUSED, PyObject *args) {
165158 safe_close (ready_read_fd , __FILE__ , __LINE__ );
166159
167160 // Close any extra fds inherited from parent
168- for (int c = min_closed_fd ; c < 201 ; c ++ ) safe_close (c , __FILE__ , __LINE__ );
161+ if (has_preserved_fds ) { for (int c = 3 ; c < 256 ; c ++ ) { if (!FD_ISSET (c , & passed_fds )) safe_close (c , __FILE__ , __LINE__ ); } }
162+ else for (int c = 3 ; c < 256 ; c ++ ) { safe_close (c , __FILE__ , __LINE__ ); }
169163
170- environ = env ;
171- execvp (exe , argv );
164+ execvpe (exe , argv , env );
172165 // Report the failure and exec kitten instead, so that we are not left
173166 // with a forked but not exec'ed process
174167 write_to_stderr ("Failed to launch child: " );
@@ -196,32 +189,14 @@ spawn(PyObject *self UNUSED, PyObject *args) {
196189 break ;
197190 }
198191#undef exit_on_err
199- free_string_tuple (argv );
200- free_string_tuple (env );
192+ free (argv );
193+ free (env );
201194 if (PyErr_Occurred ()) return NULL ;
202195 return PyLong_FromLong (pid );
203196}
204197
205- #ifdef __APPLE__
206- #include <crt_externs.h>
207- #else
208- extern char * * environ ;
209- #endif
210-
211- static PyObject *
212- clearenv_py (PyObject * self UNUSED , PyObject * args UNUSED ) {
213- #ifdef __APPLE__
214- char * * e = * _NSGetEnviron ();
215- if (e ) * e = NULL ;
216- #else
217- if (environ ) * environ = NULL ;
218- #endif
219- Py_RETURN_NONE ;
220- }
221-
222198static PyMethodDef module_methods [] = {
223199 METHODB (spawn , METH_VARARGS ),
224- {"clearenv" , clearenv_py , METH_NOARGS , "" },
225200 {NULL , NULL , 0 , NULL } /* Sentinel */
226201};
227202
0 commit comments