1 2 /* 3 * Copyright (C) NGINX, Inc. 4 */ 5 6 7 #include <Python.h> 8 9 #include <nxt_main.h> 10 #include <nxt_router.h> 11 #include <nxt_unit.h> 12 13 #include <python/nxt_python.h> 14 15 #include NXT_PYTHON_MOUNTS_H 16 17 18 static nxt_int_t nxt_python_start(nxt_task_t *task, 19 nxt_process_data_t *data); 20 static void nxt_python_atexit(void); 21 22 static uint32_t compat[] = { 23 NXT_VERNUM, NXT_DEBUG, 24 }; 25 26 27 NXT_EXPORT nxt_app_module_t nxt_app_module = { 28 sizeof(compat), 29 compat, 30 nxt_string("python"), 31 PY_VERSION, 32 nxt_python_mounts, 33 nxt_nitems(nxt_python_mounts), 34 NULL, 35 nxt_python_start, 36 }; 37 38 static PyObject *nxt_py_stderr_flush; 39 PyObject *nxt_py_application; 40 41 #if PY_MAJOR_VERSION == 3 42 static wchar_t *nxt_py_home; 43 #else 44 static char *nxt_py_home; 45 #endif 46 47 48 static nxt_int_t 49 nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) 50 { 51 int rc, asgi; 52 char *nxt_py_module; 53 size_t len; 54 PyObject *obj, *pypath, *module; 55 const char *callable; 56 nxt_unit_ctx_t *unit_ctx; 57 nxt_unit_init_t python_init; 58 nxt_common_app_conf_t *app_conf; 59 nxt_python_app_conf_t *c; 60 #if PY_MAJOR_VERSION == 3 61 char *path; 62 size_t size; 63 nxt_int_t pep405; 64 65 static const char pyvenv[] = "/pyvenv.cfg"; 66 static const char bin_python[] = "/bin/python"; 67 #endif 68 69 app_conf = data->app; 70 c = &app_conf->u.python; 71 72 if (c->home != NULL) { 73 len = nxt_strlen(c->home); 74 75 #if PY_MAJOR_VERSION == 3 76 77 path = nxt_malloc(len + sizeof(pyvenv)); 78 if (nxt_slow_path(path == NULL)) { 79 nxt_alert(task, "Failed to allocate memory"); 80 return NXT_ERROR; 81 } 82 83 nxt_memcpy(path, c->home, len); 84 nxt_memcpy(path + len, pyvenv, sizeof(pyvenv)); 85 86 pep405 = (access(path, R_OK) == 0); 87 88 nxt_free(path); 89 90 if (pep405) { 91 size = (len + sizeof(bin_python)) * sizeof(wchar_t); 92 93 } else { 94 size = (len + 1) * sizeof(wchar_t); 95 } 96 97 nxt_py_home = nxt_malloc(size); 98 if (nxt_slow_path(nxt_py_home == NULL)) { 99 nxt_alert(task, "Failed to allocate memory"); 100 return NXT_ERROR; 101 } 102 103 if (pep405) { 104 mbstowcs(nxt_py_home, c->home, len); 105 mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python)); 106 Py_SetProgramName(nxt_py_home); 107 108 } else { 109 mbstowcs(nxt_py_home, c->home, len + 1); 110 Py_SetPythonHome(nxt_py_home); 111 } 112 113 #else 114 nxt_py_home = nxt_malloc(len + 1); 115 if (nxt_slow_path(nxt_py_home == NULL)) { 116 nxt_alert(task, "Failed to allocate memory"); 117 return NXT_ERROR; 118 } 119 120 nxt_memcpy(nxt_py_home, c->home, len + 1); 121 Py_SetPythonHome(nxt_py_home); 122 #endif 123 } 124 125 Py_InitializeEx(0); 126 127 module = NULL; 128 obj = NULL; 129 130 obj = PySys_GetObject((char *) "stderr"); 131 if (nxt_slow_path(obj == NULL)) { 132 nxt_alert(task, "Python failed to get \"sys.stderr\" object"); 133 goto fail; 134 } 135 136 nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush"); 137 if (nxt_slow_path(nxt_py_stderr_flush == NULL)) { 138 nxt_alert(task, "Python failed to get \"flush\" attribute of " 139 "\"sys.stderr\" object"); 140 goto fail; 141 } 142 143 /* obj is a Borrowed reference. */ 144 145 if (c->path.length > 0) { 146 obj = PyString_FromStringAndSize((char *) c->path.start, 147 c->path.length); 148 149 if (nxt_slow_path(obj == NULL)) { 150 nxt_alert(task, "Python failed to create string object \"%V\"", 151 &c->path); 152 goto fail; 153 } 154 155 pypath = PySys_GetObject((char *) "path"); 156 157 if (nxt_slow_path(pypath == NULL)) { 158 nxt_alert(task, "Python failed to get \"sys.path\" list"); 159 goto fail; 160 } 161 162 if (nxt_slow_path(PyList_Insert(pypath, 0, obj) != 0)) { 163 nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"", 164 &c->path); 165 goto fail; 166 } 167 168 Py_DECREF(obj); 169 } 170 171 obj = Py_BuildValue("[s]", "unit"); 172 if (nxt_slow_path(obj == NULL)) { 173 nxt_alert(task, "Python failed to create the \"sys.argv\" list"); 174 goto fail; 175 } 176 177 if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) { 178 nxt_alert(task, "Python failed to set the \"sys.argv\" list"); 179 goto fail; 180 } 181 182 Py_CLEAR(obj); 183 184 nxt_py_module = nxt_alloca(c->module.length + 1); 185 nxt_memcpy(nxt_py_module, c->module.start, c->module.length); 186 nxt_py_module[c->module.length] = '\0'; 187 188 module = PyImport_ImportModule(nxt_py_module); 189 if (nxt_slow_path(module == NULL)) { 190 nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module); 191 nxt_python_print_exception(); 192 goto fail; 193 } 194 195 callable = (c->callable != NULL) ? c->callable : "application"; 196 197 obj = PyDict_GetItemString(PyModule_GetDict(module), callable); 198 if (nxt_slow_path(obj == NULL)) { 199 nxt_alert(task, "Python failed to get \"%s\" " 200 "from module \"%s\"", callable, nxt_py_module); 201 goto fail; 202 } 203 204 if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 205 nxt_alert(task, "\"%s\" in module \"%s\" " 206 "is not a callable object", callable, nxt_py_module); 207 goto fail; 208 } 209 210 nxt_py_application = obj; 211 obj = NULL; 212 213 Py_INCREF(nxt_py_application); 214 215 Py_CLEAR(module); 216 217 nxt_unit_default_init(task, &python_init); 218 219 python_init.shm_limit = data->app->shm_limit; 220 221 asgi = nxt_python_asgi_check(nxt_py_application); 222 223 if (asgi) { 224 rc = nxt_python_asgi_init(task, &python_init); 225 226 } else { 227 rc = nxt_python_wsgi_init(task, &python_init); 228 } 229 230 if (nxt_slow_path(rc == NXT_ERROR)) { 231 goto fail; 232 } 233 234 unit_ctx = nxt_unit_init(&python_init); 235 if (nxt_slow_path(unit_ctx == NULL)) { 236 goto fail; 237 } 238 239 if (asgi) { 240 rc = nxt_python_asgi_run(unit_ctx); 241 242 } else { 243 rc = nxt_python_wsgi_run(unit_ctx); 244 } 245 246 nxt_unit_done(unit_ctx); 247 248 nxt_python_atexit(); 249 250 exit(rc); 251 252 return NXT_OK; 253 254 fail: 255 256 Py_XDECREF(obj); 257 Py_XDECREF(module); 258 259 nxt_python_atexit(); 260 261 return NXT_ERROR; 262 } 263 264 265 nxt_int_t 266 nxt_python_init_strings(nxt_python_string_t *pstr) 267 { 268 PyObject *obj; 269 270 while (pstr->string.start != NULL) { 271 obj = PyString_FromStringAndSize((char *) pstr->string.start, 272 pstr->string.length); 273 if (nxt_slow_path(obj == NULL)) { 274 return NXT_ERROR; 275 } 276 277 PyUnicode_InternInPlace(&obj); 278 279 *pstr->object_p = obj; 280 281 pstr++; 282 } 283 284 return NXT_OK; 285 } 286 287 288 void 289 nxt_python_done_strings(nxt_python_string_t *pstr) 290 { 291 PyObject *obj; 292 293 while (pstr->string.start != NULL) { 294 obj = *pstr->object_p; 295 296 Py_XDECREF(obj); 297 *pstr->object_p = NULL; 298 299 pstr++; 300 } 301 } 302 303 304 static void 305 nxt_python_atexit(void) 306 { 307 nxt_python_wsgi_done(); 308 nxt_python_asgi_done(); 309 310 Py_XDECREF(nxt_py_stderr_flush); 311 Py_XDECREF(nxt_py_application); 312 313 Py_Finalize(); 314 315 if (nxt_py_home != NULL) { 316 nxt_free(nxt_py_home); 317 } 318 } 319 320 321 void 322 nxt_python_print_exception(void) 323 { 324 PyErr_Print(); 325 326 #if PY_MAJOR_VERSION == 3 327 /* The backtrace may be buffered in sys.stderr file object. */ 328 { 329 PyObject *result; 330 331 result = PyObject_CallFunction(nxt_py_stderr_flush, NULL); 332 if (nxt_slow_path(result == NULL)) { 333 PyErr_Clear(); 334 return; 335 } 336 337 Py_DECREF(result); 338 } 339 #endif 340 } 341