/* * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #include NXT_PYTHON_MOUNTS_H static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data); static void nxt_python_atexit(void); static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; NXT_EXPORT nxt_app_module_t nxt_app_module = { sizeof(compat), compat, nxt_string("python"), PY_VERSION, nxt_python_mounts, nxt_nitems(nxt_python_mounts), NULL, nxt_python_start, }; static PyObject *nxt_py_stderr_flush; PyObject *nxt_py_application; #if PY_MAJOR_VERSION == 3 static wchar_t *nxt_py_home; #else static char *nxt_py_home; #endif static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) { int rc, asgi; char *nxt_py_module; size_t len; PyObject *obj, *pypath, *module; const char *callable; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t python_init; nxt_common_app_conf_t *app_conf; nxt_python_app_conf_t *c; #if PY_MAJOR_VERSION == 3 char *path; size_t size; nxt_int_t pep405; static const char pyvenv[] = "/pyvenv.cfg"; static const char bin_python[] = "/bin/python"; #endif app_conf = data->app; c = &app_conf->u.python; if (c->home != NULL) { len = nxt_strlen(c->home); #if PY_MAJOR_VERSION == 3 path = nxt_malloc(len + sizeof(pyvenv)); if (nxt_slow_path(path == NULL)) { nxt_alert(task, "Failed to allocate memory"); return NXT_ERROR; } nxt_memcpy(path, c->home, len); nxt_memcpy(path + len, pyvenv, sizeof(pyvenv)); pep405 = (access(path, R_OK) == 0); nxt_free(path); if (pep405) { size = (len + sizeof(bin_python)) * sizeof(wchar_t); } else { size = (len + 1) * sizeof(wchar_t); } nxt_py_home = nxt_malloc(size); if (nxt_slow_path(nxt_py_home == NULL)) { nxt_alert(task, "Failed to allocate memory"); return NXT_ERROR; } if (pep405) { mbstowcs(nxt_py_home, c->home, len); mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python)); Py_SetProgramName(nxt_py_home); } else { mbstowcs(nxt_py_home, c->home, len + 1); Py_SetPythonHome(nxt_py_home); } #else nxt_py_home = nxt_malloc(len + 1); if (nxt_slow_path(nxt_py_home == NULL)) { nxt_alert(task, "Failed to allocate memory"); return NXT_ERROR; } nxt_memcpy(nxt_py_home, c->home, len + 1); Py_SetPythonHome(nxt_py_home); #endif } Py_InitializeEx(0); module = NULL; obj = NULL; obj = PySys_GetObject((char *) "stderr"); if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to get \"sys.stderr\" object"); goto fail; } nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush"); if (nxt_slow_path(nxt_py_stderr_flush == NULL)) { nxt_alert(task, "Python failed to get \"flush\" attribute of " "\"sys.stderr\" object"); goto fail; } /* obj is a Borrowed reference. */ if (c->path.length > 0) { obj = PyString_FromStringAndSize((char *) c->path.start, c->path.length); if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to create string object \"%V\"", &c->path); goto fail; } pypath = PySys_GetObject((char *) "path"); if (nxt_slow_path(pypath == NULL)) { nxt_alert(task, "Python failed to get \"sys.path\" list"); goto fail; } if (nxt_slow_path(PyList_Insert(pypath, 0, obj) != 0)) { nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"", &c->path); goto fail; } Py_DECREF(obj); } obj = Py_BuildValue("[s]", "unit"); if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to create the \"sys.argv\" list"); goto fail; } if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) { nxt_alert(task, "Python failed to set the \"sys.argv\" list"); goto fail; } Py_CLEAR(obj); nxt_py_module = nxt_alloca(c->module.length + 1); nxt_memcpy(nxt_py_module, c->module.start, c->module.length); nxt_py_module[c->module.length] = '\0'; module = PyImport_ImportModule(nxt_py_module); if (nxt_slow_path(module == NULL)) { nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module); nxt_python_print_exception(); goto fail; } callable = (c->callable != NULL) ? c->callable : "application"; obj = PyDict_GetItemString(PyModule_GetDict(module), callable); if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to get \"%s\" " "from module \"%s\"", callable, nxt_py_module); goto fail; } if (nxt_slow_path(PyCallable_Check(obj) == 0)) { nxt_alert(task, "\"%s\" in module \"%s\" " "is not a callable object", callable, nxt_py_module); goto fail; } nxt_py_application = obj; obj = NULL; Py_INCREF(nxt_py_application); Py_CLEAR(module); nxt_unit_default_init(task, &python_init); python_init.shm_limit = data->app->shm_limit; asgi = nxt_python_asgi_check(nxt_py_application); if (asgi) { rc = nxt_python_asgi_init(task, &python_init); } else { rc = nxt_python_wsgi_init(task, &python_init); } if (nxt_slow_path(rc == NXT_ERROR)) { goto fail; } unit_ctx = nxt_unit_init(&python_init); if (nxt_slow_path(unit_ctx == NULL)) { goto fail; } if (asgi) { rc = nxt_python_asgi_run(unit_ctx); } else { rc = nxt_python_wsgi_run(unit_ctx); } nxt_unit_done(unit_ctx); nxt_python_atexit(); exit(rc); return NXT_OK; fail: Py_XDECREF(obj); Py_XDECREF(module); nxt_python_atexit(); return NXT_ERROR; } nxt_int_t nxt_python_init_strings(nxt_python_string_t *pstr) { PyObject *obj; while (pstr->string.start != NULL) { obj = PyString_FromStringAndSize((char *) pstr->string.start, pstr->string.length); if (nxt_slow_path(obj == NULL)) { return NXT_ERROR; } PyUnicode_InternInPlace(&obj); *pstr->object_p = obj; pstr++; } return NXT_OK; } void nxt_python_done_strings(nxt_python_string_t *pstr) { PyObject *obj; while (pstr->string.start != NULL) { obj = *pstr->object_p; Py_XDECREF(obj); *pstr->object_p = NULL; pstr++; } } static void nxt_python_atexit(void) { nxt_python_wsgi_done(); nxt_python_asgi_done(); Py_XDECREF(nxt_py_stderr_flush); Py_XDECREF(nxt_py_application); Py_Finalize(); if (nxt_py_home != NULL) { nxt_free(nxt_py_home); } } void nxt_python_print_exception(void) { PyErr_Print(); #if PY_MAJOR_VERSION == 3 /* The backtrace may be buffered in sys.stderr file object. */ { PyObject *result; result = PyObject_CallFunction(nxt_py_stderr_flush, NULL); if (nxt_slow_path(result == NULL)) { PyErr_Clear(); return; } Py_DECREF(result); } #endif }