@@ -176,6 +176,7 @@ corresponding Unix manual entries for more information on calls.");
176176#else
177177/* Unix functions that the configure script doesn't check for */
178178#define HAVE_EXECV 1
179+ #define HAVE_POSIX_SPAWN 1
179180#define HAVE_FORK 1
180181#if defined(__USLC__ ) && defined(__SCO_VERSION__ ) /* SCO UDK Compiler */
181182#define HAVE_FORK1 1
@@ -246,6 +247,10 @@ extern int lstat(const char *, struct stat *);
246247
247248#endif /* !_MSC_VER */
248249
250+ #ifdef HAVE_POSIX_SPAWN
251+ #include <spawn.h>
252+ #endif
253+
249254#ifdef HAVE_UTIME_H
250255#include <utime.h>
251256#endif /* HAVE_UTIME_H */
@@ -5097,6 +5102,194 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env)
50975102
50985103#endif /* HAVE_EXECV */
50995104
5105+ #ifdef HAVE_POSIX_SPAWN
5106+
5107+ enum posix_spawn_file_actions_identifier {
5108+ POSIX_SPAWN_OPEN ,
5109+ POSIX_SPAWN_CLOSE ,
5110+ POSIX_SPAWN_DUP2
5111+ };
5112+
5113+ /*[clinic input]
5114+
5115+ os.posix_spawn
5116+ path: path_t
5117+ Path of executable file.
5118+ argv: object
5119+ Tuple or list of strings.
5120+ env: object
5121+ Dictionary of strings mapping to strings.
5122+ file_actions: object = None
5123+ FileActions object.
5124+ /
5125+
5126+ Execute the program specified by path in a new process.
5127+ [clinic start generated code]*/
5128+
5129+ static PyObject *
5130+ os_posix_spawn_impl (PyObject * module , path_t * path , PyObject * argv ,
5131+ PyObject * env , PyObject * file_actions )
5132+ /*[clinic end generated code: output=d023521f541c709c input=0ec9f1cfdc890be5]*/
5133+ {
5134+ EXECV_CHAR * * argvlist = NULL ;
5135+ EXECV_CHAR * * envlist ;
5136+ Py_ssize_t argc , envc ;
5137+
5138+ /* posix_spawn has three arguments: (path, argv, env), where
5139+ argv is a list or tuple of strings and env is a dictionary
5140+ like posix.environ. */
5141+
5142+ if (!PySequence_Check (argv )){
5143+ PyErr_SetString (PyExc_TypeError ,
5144+ "posix_spawn: argv must be a tuple or list" );
5145+ goto fail ;
5146+ }
5147+ argc = PySequence_Size (argv );
5148+ if (argc < 1 ) {
5149+ PyErr_SetString (PyExc_ValueError , "posix_spawn: argv must not be empty" );
5150+ return NULL ;
5151+ }
5152+
5153+ if (!PyMapping_Check (env )) {
5154+ PyErr_SetString (PyExc_TypeError ,
5155+ "posix_spawn: environment must be a mapping object" );
5156+ goto fail ;
5157+ }
5158+
5159+ argvlist = parse_arglist (argv , & argc );
5160+ if (argvlist == NULL ) {
5161+ goto fail ;
5162+ }
5163+ if (!argvlist [0 ][0 ]) {
5164+ PyErr_SetString (PyExc_ValueError ,
5165+ "posix_spawn: argv first element cannot be empty" );
5166+ goto fail ;
5167+ }
5168+
5169+ envlist = parse_envlist (env , & envc );
5170+ if (envlist == NULL )
5171+ goto fail ;
5172+
5173+ pid_t pid ;
5174+ posix_spawn_file_actions_t * file_actionsp = NULL ;
5175+ if (file_actions != NULL && file_actions != Py_None ){
5176+ posix_spawn_file_actions_t _file_actions ;
5177+ if (posix_spawn_file_actions_init (& _file_actions ) != 0 ){
5178+ PyErr_SetString (PyExc_TypeError ,
5179+ "Error initializing file actions" );
5180+ goto fail ;
5181+ }
5182+
5183+
5184+ file_actionsp = & _file_actions ;
5185+
5186+
5187+ PyObject * seq = PySequence_Fast (file_actions , "file_actions must be a sequence" );
5188+ if (seq == NULL ){
5189+ goto fail ;
5190+ }
5191+ PyObject * file_actions_obj ;
5192+ PyObject * mode_obj ;
5193+
5194+ for (int i = 0 ; i < PySequence_Fast_GET_SIZE (seq ); ++ i ) {
5195+ file_actions_obj = PySequence_Fast_GET_ITEM (seq , i );
5196+
5197+ if (!PySequence_Check (file_actions_obj ) | !PySequence_Size (file_actions_obj )){
5198+ PyErr_SetString (PyExc_TypeError ,"Each file_action element must be a non empty sequence" );
5199+ goto fail ;
5200+ }
5201+
5202+
5203+ mode_obj = PySequence_Fast_GET_ITEM (file_actions_obj , 0 );
5204+ int mode = PyLong_AsLong (mode_obj );
5205+
5206+ /* Populate the file_actions object */
5207+
5208+ switch (mode ) {
5209+
5210+ case POSIX_SPAWN_OPEN :
5211+ if (PySequence_Size (file_actions_obj ) != 5 ){
5212+ PyErr_SetString (PyExc_TypeError ,"A open file_action object must have 5 elements" );
5213+ goto fail ;
5214+ }
5215+
5216+ long open_fd = PyLong_AsLong (PySequence_GetItem (file_actions_obj , 1 ));
5217+ if (PyErr_Occurred ()) {
5218+ goto fail ;
5219+ }
5220+ const char * open_path = PyUnicode_AsUTF8 (PySequence_GetItem (file_actions_obj , 2 ));
5221+ if (open_path == NULL ){
5222+ goto fail ;
5223+ }
5224+ long open_oflag = PyLong_AsLong (PySequence_GetItem (file_actions_obj , 3 ));
5225+ if (PyErr_Occurred ()) {
5226+ goto fail ;
5227+ }
5228+ long open_mode = PyLong_AsLong (PySequence_GetItem (file_actions_obj , 4 ));
5229+ if (PyErr_Occurred ()) {
5230+ goto fail ;
5231+ }
5232+ posix_spawn_file_actions_addopen (file_actionsp , open_fd , open_path , open_oflag , open_mode );
5233+ break ;
5234+
5235+ case POSIX_SPAWN_CLOSE :
5236+ if (PySequence_Size (file_actions_obj ) != 2 ){
5237+ PyErr_SetString (PyExc_TypeError ,"A close file_action object must have 2 elements" );
5238+ goto fail ;
5239+ }
5240+
5241+ long close_fd = PyLong_AsLong (PySequence_GetItem (file_actions_obj , 1 ));
5242+ if (PyErr_Occurred ()) {
5243+ goto fail ;
5244+ }
5245+ posix_spawn_file_actions_addclose (file_actionsp , close_fd );
5246+ break ;
5247+
5248+ case POSIX_SPAWN_DUP2 :
5249+ if (PySequence_Size (file_actions_obj ) != 3 ){
5250+ PyErr_SetString (PyExc_TypeError ,"A dup2 file_action object must have 3 elements" );
5251+ goto fail ;
5252+ }
5253+
5254+ long fd1 = PyLong_AsLong (PySequence_GetItem (file_actions_obj , 1 ));
5255+ if (PyErr_Occurred ()) {
5256+ goto fail ;
5257+ }
5258+ long fd2 = PyLong_AsLong (PySequence_GetItem (file_actions_obj , 2 ));
5259+ if (PyErr_Occurred ()) {
5260+ goto fail ;
5261+ }
5262+ posix_spawn_file_actions_adddup2 (file_actionsp , fd1 , fd2 );
5263+ break ;
5264+
5265+ default :
5266+ PyErr_SetString (PyExc_TypeError ,"Unknown file_actions identifier" );
5267+ goto fail ;
5268+ }
5269+ }
5270+ Py_DECREF (seq );
5271+ }
5272+
5273+ _Py_BEGIN_SUPPRESS_IPH
5274+ posix_spawn (& pid , path -> narrow , file_actionsp , NULL , argvlist , envlist );
5275+ return PyLong_FromPid (pid );
5276+ _Py_END_SUPPRESS_IPH
5277+
5278+ path_error (path );
5279+
5280+ free_string_array (envlist , envc );
5281+
5282+ fail :
5283+
5284+ if (argvlist ) {
5285+ free_string_array (argvlist , argc );
5286+ }
5287+ return NULL ;
5288+
5289+
5290+ }
5291+ #endif /* HAVE_POSIX_SPAWN */
5292+
51005293
51015294#if defined(HAVE_SPAWNV ) || defined(HAVE_WSPAWNV )
51025295/*[clinic input]
@@ -5189,7 +5382,6 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv)
51895382 return Py_BuildValue (_Py_PARSE_INTPTR , spawnval );
51905383}
51915384
5192-
51935385/*[clinic input]
51945386os.spawnve
51955387
@@ -12610,6 +12802,7 @@ static PyMethodDef posix_methods[] = {
1261012802 OS_NICE_METHODDEF
1261112803 OS_GETPRIORITY_METHODDEF
1261212804 OS_SETPRIORITY_METHODDEF
12805+ OS_POSIX_SPAWN_METHODDEF
1261312806#ifdef HAVE_READLINK
1261412807 {"readlink" , (PyCFunction )posix_readlink ,
1261512808 METH_VARARGS | METH_KEYWORDS ,
@@ -13164,6 +13357,13 @@ all_ins(PyObject *m)
1316413357 if (PyModule_AddIntConstant (m , "RWF_NOWAIT" , RWF_NOWAIT )) return -1 ;
1316513358#endif
1316613359
13360+ /* constants for posix_spawn */
13361+ #ifdef HAVE_POSIX_SPAWN
13362+ if (PyModule_AddIntConstant (m , "POSIX_SPAWN_OPEN" , POSIX_SPAWN_OPEN )) return -1 ;
13363+ if (PyModule_AddIntConstant (m , "POSIX_SPAWN_CLOSE" , POSIX_SPAWN_CLOSE )) return -1 ;
13364+ if (PyModule_AddIntConstant (m , "POSIX_SPAWN_DUP2" , POSIX_SPAWN_DUP2 )) return -1 ;
13365+ #endif
13366+
1316713367#ifdef HAVE_SPAWNV
1316813368 if (PyModule_AddIntConstant (m , "P_WAIT" , _P_WAIT )) return -1 ;
1316913369 if (PyModule_AddIntConstant (m , "P_NOWAIT" , _P_NOWAIT )) return -1 ;
0 commit comments