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