11592Smax.romanov@nginx.com 21592Smax.romanov@nginx.com /* 31592Smax.romanov@nginx.com * Copyright (C) NGINX, Inc. 41592Smax.romanov@nginx.com */ 51592Smax.romanov@nginx.com 61592Smax.romanov@nginx.com 71592Smax.romanov@nginx.com #include <Python.h> 81592Smax.romanov@nginx.com 91592Smax.romanov@nginx.com #include <nxt_main.h> 101592Smax.romanov@nginx.com #include <nxt_router.h> 111592Smax.romanov@nginx.com #include <nxt_unit.h> 121592Smax.romanov@nginx.com 131592Smax.romanov@nginx.com #include <python/nxt_python.h> 141592Smax.romanov@nginx.com 151592Smax.romanov@nginx.com #include NXT_PYTHON_MOUNTS_H 161592Smax.romanov@nginx.com 171592Smax.romanov@nginx.com 181681Smax.romanov@nginx.com typedef struct { 191681Smax.romanov@nginx.com pthread_t thread; 201681Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 211681Smax.romanov@nginx.com void *ctx_data; 221681Smax.romanov@nginx.com } nxt_py_thread_info_t; 231681Smax.romanov@nginx.com 241681Smax.romanov@nginx.com 251592Smax.romanov@nginx.com static nxt_int_t nxt_python_start(nxt_task_t *task, 261592Smax.romanov@nginx.com nxt_process_data_t *data); 271872So.canty@f5.com static nxt_int_t nxt_python_set_target(nxt_task_t *task, 281872So.canty@f5.com nxt_python_target_t *target, nxt_conf_value_t *conf); 291759Svbart@nginx.com static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value); 301681Smax.romanov@nginx.com static int nxt_python_init_threads(nxt_python_app_conf_t *c); 311681Smax.romanov@nginx.com static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx); 321681Smax.romanov@nginx.com static void *nxt_python_thread_func(void *main_ctx); 331681Smax.romanov@nginx.com static void nxt_python_join_threads(nxt_unit_ctx_t *ctx, 341681Smax.romanov@nginx.com nxt_python_app_conf_t *c); 351592Smax.romanov@nginx.com static void nxt_python_atexit(void); 361592Smax.romanov@nginx.com 371592Smax.romanov@nginx.com static uint32_t compat[] = { 381592Smax.romanov@nginx.com NXT_VERNUM, NXT_DEBUG, 391592Smax.romanov@nginx.com }; 401592Smax.romanov@nginx.com 411592Smax.romanov@nginx.com 421592Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t nxt_app_module = { 431592Smax.romanov@nginx.com sizeof(compat), 441592Smax.romanov@nginx.com compat, 451592Smax.romanov@nginx.com nxt_string("python"), 461592Smax.romanov@nginx.com PY_VERSION, 471592Smax.romanov@nginx.com nxt_python_mounts, 481592Smax.romanov@nginx.com nxt_nitems(nxt_python_mounts), 491592Smax.romanov@nginx.com NULL, 501592Smax.romanov@nginx.com nxt_python_start, 511592Smax.romanov@nginx.com }; 521592Smax.romanov@nginx.com 531592Smax.romanov@nginx.com static PyObject *nxt_py_stderr_flush; 541872So.canty@f5.com nxt_python_targets_t *nxt_py_targets; 551592Smax.romanov@nginx.com 561592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 571592Smax.romanov@nginx.com static wchar_t *nxt_py_home; 581592Smax.romanov@nginx.com #else 591592Smax.romanov@nginx.com static char *nxt_py_home; 601592Smax.romanov@nginx.com #endif 611592Smax.romanov@nginx.com 621681Smax.romanov@nginx.com static pthread_attr_t *nxt_py_thread_attr; 631681Smax.romanov@nginx.com static nxt_py_thread_info_t *nxt_py_threads; 641681Smax.romanov@nginx.com static nxt_python_proto_t nxt_py_proto; 651681Smax.romanov@nginx.com 661592Smax.romanov@nginx.com 671592Smax.romanov@nginx.com static nxt_int_t 681592Smax.romanov@nginx.com nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) 691592Smax.romanov@nginx.com { 701681Smax.romanov@nginx.com int rc; 711872So.canty@f5.com size_t len, size; 721872So.canty@f5.com uint32_t next; 731759Svbart@nginx.com PyObject *obj, *module; 741872So.canty@f5.com nxt_str_t proto, probe_proto, name; 751872So.canty@f5.com nxt_int_t ret, n, i; 761592Smax.romanov@nginx.com nxt_unit_ctx_t *unit_ctx; 771592Smax.romanov@nginx.com nxt_unit_init_t python_init; 781872So.canty@f5.com nxt_conf_value_t *cv; 791872So.canty@f5.com nxt_python_targets_t *targets; 801592Smax.romanov@nginx.com nxt_common_app_conf_t *app_conf; 811592Smax.romanov@nginx.com nxt_python_app_conf_t *c; 821592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 831592Smax.romanov@nginx.com char *path; 841592Smax.romanov@nginx.com nxt_int_t pep405; 851592Smax.romanov@nginx.com 861592Smax.romanov@nginx.com static const char pyvenv[] = "/pyvenv.cfg"; 871592Smax.romanov@nginx.com static const char bin_python[] = "/bin/python"; 881592Smax.romanov@nginx.com #endif 891592Smax.romanov@nginx.com 901697Smax.romanov@nginx.com static const nxt_str_t wsgi = nxt_string("wsgi"); 911697Smax.romanov@nginx.com static const nxt_str_t asgi = nxt_string("asgi"); 921697Smax.romanov@nginx.com 931592Smax.romanov@nginx.com app_conf = data->app; 941592Smax.romanov@nginx.com c = &app_conf->u.python; 951592Smax.romanov@nginx.com 961592Smax.romanov@nginx.com if (c->home != NULL) { 971592Smax.romanov@nginx.com len = nxt_strlen(c->home); 981592Smax.romanov@nginx.com 991592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 1001592Smax.romanov@nginx.com 1011592Smax.romanov@nginx.com path = nxt_malloc(len + sizeof(pyvenv)); 1021592Smax.romanov@nginx.com if (nxt_slow_path(path == NULL)) { 1031592Smax.romanov@nginx.com nxt_alert(task, "Failed to allocate memory"); 1041592Smax.romanov@nginx.com return NXT_ERROR; 1051592Smax.romanov@nginx.com } 1061592Smax.romanov@nginx.com 1071592Smax.romanov@nginx.com nxt_memcpy(path, c->home, len); 1081592Smax.romanov@nginx.com nxt_memcpy(path + len, pyvenv, sizeof(pyvenv)); 1091592Smax.romanov@nginx.com 1101592Smax.romanov@nginx.com pep405 = (access(path, R_OK) == 0); 1111592Smax.romanov@nginx.com 1121592Smax.romanov@nginx.com nxt_free(path); 1131592Smax.romanov@nginx.com 1141592Smax.romanov@nginx.com if (pep405) { 1151592Smax.romanov@nginx.com size = (len + sizeof(bin_python)) * sizeof(wchar_t); 1161592Smax.romanov@nginx.com 1171592Smax.romanov@nginx.com } else { 1181592Smax.romanov@nginx.com size = (len + 1) * sizeof(wchar_t); 1191592Smax.romanov@nginx.com } 1201592Smax.romanov@nginx.com 1211592Smax.romanov@nginx.com nxt_py_home = nxt_malloc(size); 1221592Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_home == NULL)) { 1231592Smax.romanov@nginx.com nxt_alert(task, "Failed to allocate memory"); 1241592Smax.romanov@nginx.com return NXT_ERROR; 1251592Smax.romanov@nginx.com } 1261592Smax.romanov@nginx.com 1271592Smax.romanov@nginx.com if (pep405) { 1281592Smax.romanov@nginx.com mbstowcs(nxt_py_home, c->home, len); 1291592Smax.romanov@nginx.com mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python)); 1301592Smax.romanov@nginx.com Py_SetProgramName(nxt_py_home); 1311592Smax.romanov@nginx.com 1321592Smax.romanov@nginx.com } else { 1331592Smax.romanov@nginx.com mbstowcs(nxt_py_home, c->home, len + 1); 1341592Smax.romanov@nginx.com Py_SetPythonHome(nxt_py_home); 1351592Smax.romanov@nginx.com } 1361592Smax.romanov@nginx.com 1371592Smax.romanov@nginx.com #else 1381592Smax.romanov@nginx.com nxt_py_home = nxt_malloc(len + 1); 1391592Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_home == NULL)) { 1401592Smax.romanov@nginx.com nxt_alert(task, "Failed to allocate memory"); 1411592Smax.romanov@nginx.com return NXT_ERROR; 1421592Smax.romanov@nginx.com } 1431592Smax.romanov@nginx.com 1441592Smax.romanov@nginx.com nxt_memcpy(nxt_py_home, c->home, len + 1); 1451592Smax.romanov@nginx.com Py_SetPythonHome(nxt_py_home); 1461592Smax.romanov@nginx.com #endif 1471592Smax.romanov@nginx.com } 1481592Smax.romanov@nginx.com 1491592Smax.romanov@nginx.com Py_InitializeEx(0); 1501592Smax.romanov@nginx.com 1511681Smax.romanov@nginx.com #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7) 1521681Smax.romanov@nginx.com if (c->threads > 1) { 1531681Smax.romanov@nginx.com PyEval_InitThreads(); 1541681Smax.romanov@nginx.com } 1551681Smax.romanov@nginx.com #endif 1561681Smax.romanov@nginx.com 1571592Smax.romanov@nginx.com module = NULL; 1581592Smax.romanov@nginx.com obj = NULL; 1591592Smax.romanov@nginx.com 1601681Smax.romanov@nginx.com python_init.ctx_data = NULL; 1611681Smax.romanov@nginx.com 1621592Smax.romanov@nginx.com obj = PySys_GetObject((char *) "stderr"); 1631592Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 1641592Smax.romanov@nginx.com nxt_alert(task, "Python failed to get \"sys.stderr\" object"); 1651592Smax.romanov@nginx.com goto fail; 1661592Smax.romanov@nginx.com } 1671592Smax.romanov@nginx.com 1681592Smax.romanov@nginx.com nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush"); 1691759Svbart@nginx.com 1701759Svbart@nginx.com /* obj is a Borrowed reference. */ 1711759Svbart@nginx.com obj = NULL; 1721759Svbart@nginx.com 1731592Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_stderr_flush == NULL)) { 1741592Smax.romanov@nginx.com nxt_alert(task, "Python failed to get \"flush\" attribute of " 1751592Smax.romanov@nginx.com "\"sys.stderr\" object"); 1761592Smax.romanov@nginx.com goto fail; 1771592Smax.romanov@nginx.com } 1781592Smax.romanov@nginx.com 1791759Svbart@nginx.com if (nxt_slow_path(nxt_python_set_path(task, c->path) != NXT_OK)) { 1801759Svbart@nginx.com goto fail; 1811592Smax.romanov@nginx.com } 1821592Smax.romanov@nginx.com 1831592Smax.romanov@nginx.com obj = Py_BuildValue("[s]", "unit"); 1841592Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 1851592Smax.romanov@nginx.com nxt_alert(task, "Python failed to create the \"sys.argv\" list"); 1861592Smax.romanov@nginx.com goto fail; 1871592Smax.romanov@nginx.com } 1881592Smax.romanov@nginx.com 1891592Smax.romanov@nginx.com if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) { 1901592Smax.romanov@nginx.com nxt_alert(task, "Python failed to set the \"sys.argv\" list"); 1911592Smax.romanov@nginx.com goto fail; 1921592Smax.romanov@nginx.com } 1931592Smax.romanov@nginx.com 1941592Smax.romanov@nginx.com Py_CLEAR(obj); 1951592Smax.romanov@nginx.com 1961872So.canty@f5.com n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1); 1971872So.canty@f5.com 1981872So.canty@f5.com size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t); 1991592Smax.romanov@nginx.com 2001872So.canty@f5.com targets = nxt_unit_malloc(NULL, size); 2011872So.canty@f5.com if (nxt_slow_path(targets == NULL)) { 2021872So.canty@f5.com nxt_alert(task, "Could not allocate targets"); 2031592Smax.romanov@nginx.com goto fail; 2041592Smax.romanov@nginx.com } 2051592Smax.romanov@nginx.com 2061872So.canty@f5.com memset(targets, 0, size); 2071601Smax.romanov@nginx.com 2081872So.canty@f5.com targets->count = n; 2091872So.canty@f5.com nxt_py_targets = targets; 2101872So.canty@f5.com 2111872So.canty@f5.com if (c->targets != NULL) { 2121872So.canty@f5.com next = 0; 2131592Smax.romanov@nginx.com 2141872So.canty@f5.com for (i = 0; /* void */; i++) { 2151872So.canty@f5.com cv = nxt_conf_next_object_member(c->targets, &name, &next); 2161872So.canty@f5.com if (cv == NULL) { 2171872So.canty@f5.com break; 2181872So.canty@f5.com } 2191592Smax.romanov@nginx.com 2201872So.canty@f5.com ret = nxt_python_set_target(task, &targets->target[i], cv); 2211872So.canty@f5.com if (nxt_slow_path(ret != NXT_OK)) { 2221872So.canty@f5.com goto fail; 2231872So.canty@f5.com } 2241872So.canty@f5.com } 2251592Smax.romanov@nginx.com 2261872So.canty@f5.com } else { 2271872So.canty@f5.com ret = nxt_python_set_target(task, &targets->target[0], app_conf->self); 2281872So.canty@f5.com if (nxt_slow_path(ret != NXT_OK)) { 2291872So.canty@f5.com goto fail; 2301872So.canty@f5.com } 2311872So.canty@f5.com } 2321592Smax.romanov@nginx.com 2331980Smax.romanov@nginx.com nxt_unit_default_init(task, &python_init, data->app); 2341592Smax.romanov@nginx.com 2351681Smax.romanov@nginx.com python_init.data = c; 2361681Smax.romanov@nginx.com python_init.callbacks.ready_handler = nxt_python_ready_handler; 2371592Smax.romanov@nginx.com 2381697Smax.romanov@nginx.com proto = c->protocol; 2391697Smax.romanov@nginx.com 2401697Smax.romanov@nginx.com if (proto.length == 0) { 2411872So.canty@f5.com proto = nxt_python_asgi_check(targets->target[0].application) 2421872So.canty@f5.com ? asgi : wsgi; 2431872So.canty@f5.com 2441872So.canty@f5.com for (i = 1; i < targets->count; i++) { 2451872So.canty@f5.com probe_proto = nxt_python_asgi_check(targets->target[i].application) 2461872So.canty@f5.com ? asgi : wsgi; 2471872So.canty@f5.com if (probe_proto.start != proto.start) { 2481872So.canty@f5.com nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, " 2491872So.canty@f5.com "specify protocol in config if incorrect"); 2501872So.canty@f5.com goto fail; 2511872So.canty@f5.com } 2521872So.canty@f5.com } 2531697Smax.romanov@nginx.com } 2541697Smax.romanov@nginx.com 2551697Smax.romanov@nginx.com if (nxt_strstr_eq(&proto, &asgi)) { 2561681Smax.romanov@nginx.com rc = nxt_python_asgi_init(&python_init, &nxt_py_proto); 2571624Smax.romanov@nginx.com 2581624Smax.romanov@nginx.com } else { 2591681Smax.romanov@nginx.com rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto); 2601681Smax.romanov@nginx.com } 2611681Smax.romanov@nginx.com 2621681Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 2631681Smax.romanov@nginx.com goto fail; 2641624Smax.romanov@nginx.com } 2651624Smax.romanov@nginx.com 2661918Smax.romanov@nginx.com rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1); 2671681Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 2681592Smax.romanov@nginx.com goto fail; 2691592Smax.romanov@nginx.com } 2701592Smax.romanov@nginx.com 2711681Smax.romanov@nginx.com rc = nxt_python_init_threads(c); 2721681Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 2731681Smax.romanov@nginx.com goto fail; 2741681Smax.romanov@nginx.com } 2751681Smax.romanov@nginx.com 2761681Smax.romanov@nginx.com if (nxt_py_proto.startup != NULL) { 2771681Smax.romanov@nginx.com if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) { 2781681Smax.romanov@nginx.com goto fail; 2791681Smax.romanov@nginx.com } 2801681Smax.romanov@nginx.com } 2811681Smax.romanov@nginx.com 2821592Smax.romanov@nginx.com unit_ctx = nxt_unit_init(&python_init); 2831592Smax.romanov@nginx.com if (nxt_slow_path(unit_ctx == NULL)) { 2841592Smax.romanov@nginx.com goto fail; 2851592Smax.romanov@nginx.com } 2861592Smax.romanov@nginx.com 2871681Smax.romanov@nginx.com rc = nxt_py_proto.run(unit_ctx); 2881624Smax.romanov@nginx.com 2891681Smax.romanov@nginx.com nxt_python_join_threads(unit_ctx, c); 2901592Smax.romanov@nginx.com 2911592Smax.romanov@nginx.com nxt_unit_done(unit_ctx); 2921592Smax.romanov@nginx.com 2931681Smax.romanov@nginx.com nxt_py_proto.ctx_data_free(python_init.ctx_data); 2941681Smax.romanov@nginx.com 2951592Smax.romanov@nginx.com nxt_python_atexit(); 2961592Smax.romanov@nginx.com 2971592Smax.romanov@nginx.com exit(rc); 2981592Smax.romanov@nginx.com 2991592Smax.romanov@nginx.com return NXT_OK; 3001592Smax.romanov@nginx.com 3011592Smax.romanov@nginx.com fail: 3021592Smax.romanov@nginx.com 3031681Smax.romanov@nginx.com nxt_python_join_threads(NULL, c); 3041681Smax.romanov@nginx.com 3051681Smax.romanov@nginx.com if (python_init.ctx_data != NULL) { 3061681Smax.romanov@nginx.com nxt_py_proto.ctx_data_free(python_init.ctx_data); 3071681Smax.romanov@nginx.com } 3081681Smax.romanov@nginx.com 3091592Smax.romanov@nginx.com Py_XDECREF(obj); 3101592Smax.romanov@nginx.com Py_XDECREF(module); 3111592Smax.romanov@nginx.com 3121592Smax.romanov@nginx.com nxt_python_atexit(); 3131592Smax.romanov@nginx.com 3141592Smax.romanov@nginx.com return NXT_ERROR; 3151592Smax.romanov@nginx.com } 3161592Smax.romanov@nginx.com 3171592Smax.romanov@nginx.com 3181759Svbart@nginx.com static nxt_int_t 3191872So.canty@f5.com nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, 3201872So.canty@f5.com nxt_conf_value_t *conf) 3211872So.canty@f5.com { 3221872So.canty@f5.com char *callable, *module_name; 3231872So.canty@f5.com PyObject *module, *obj; 3241872So.canty@f5.com nxt_str_t str; 3251872So.canty@f5.com nxt_conf_value_t *value; 3261872So.canty@f5.com 3271872So.canty@f5.com static nxt_str_t module_str = nxt_string("module"); 3281872So.canty@f5.com static nxt_str_t callable_str = nxt_string("callable"); 3291872So.canty@f5.com 3301872So.canty@f5.com module = obj = NULL; 3311872So.canty@f5.com 3321872So.canty@f5.com value = nxt_conf_get_object_member(conf, &module_str, NULL); 3331872So.canty@f5.com if (nxt_slow_path(value == NULL)) { 3341872So.canty@f5.com goto fail; 3351872So.canty@f5.com } 3361872So.canty@f5.com 3371872So.canty@f5.com nxt_conf_get_string(value, &str); 3381872So.canty@f5.com 3391872So.canty@f5.com module_name = nxt_alloca(str.length + 1); 3401872So.canty@f5.com nxt_memcpy(module_name, str.start, str.length); 3411872So.canty@f5.com module_name[str.length] = '\0'; 3421872So.canty@f5.com 3431872So.canty@f5.com module = PyImport_ImportModule(module_name); 3441872So.canty@f5.com if (nxt_slow_path(module == NULL)) { 3451872So.canty@f5.com nxt_alert(task, "Python failed to import module \"%s\"", module_name); 3461872So.canty@f5.com nxt_python_print_exception(); 3471872So.canty@f5.com goto fail; 3481872So.canty@f5.com } 3491872So.canty@f5.com 3501872So.canty@f5.com value = nxt_conf_get_object_member(conf, &callable_str, NULL); 3511872So.canty@f5.com if (value == NULL) { 3521872So.canty@f5.com callable = nxt_alloca(12); 3531872So.canty@f5.com nxt_memcpy(callable, "application", 12); 3541872So.canty@f5.com 3551872So.canty@f5.com } else { 3561872So.canty@f5.com nxt_conf_get_string(value, &str); 3571872So.canty@f5.com 3581872So.canty@f5.com callable = nxt_alloca(str.length + 1); 3591872So.canty@f5.com nxt_memcpy(callable, str.start, str.length); 3601872So.canty@f5.com callable[str.length] = '\0'; 3611872So.canty@f5.com } 3621872So.canty@f5.com 3631872So.canty@f5.com obj = PyDict_GetItemString(PyModule_GetDict(module), callable); 3641872So.canty@f5.com if (nxt_slow_path(obj == NULL)) { 3651872So.canty@f5.com nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"", 3661932Smax.romanov@nginx.com callable, module_name); 3671872So.canty@f5.com goto fail; 3681872So.canty@f5.com } 3691872So.canty@f5.com 3701872So.canty@f5.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 3711872So.canty@f5.com nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object", 3721932Smax.romanov@nginx.com callable, module_name); 3731872So.canty@f5.com goto fail; 3741872So.canty@f5.com } 3751872So.canty@f5.com 3761872So.canty@f5.com target->application = obj; 3771872So.canty@f5.com obj = NULL; 3781872So.canty@f5.com 3791872So.canty@f5.com Py_INCREF(target->application); 3801872So.canty@f5.com Py_CLEAR(module); 3811872So.canty@f5.com 3821872So.canty@f5.com return NXT_OK; 3831872So.canty@f5.com 3841872So.canty@f5.com fail: 3851872So.canty@f5.com 3861872So.canty@f5.com Py_XDECREF(obj); 3871872So.canty@f5.com Py_XDECREF(module); 3881872So.canty@f5.com 3891872So.canty@f5.com return NXT_ERROR; 3901872So.canty@f5.com } 3911872So.canty@f5.com 3921872So.canty@f5.com 3931872So.canty@f5.com static nxt_int_t 3941759Svbart@nginx.com nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value) 3951759Svbart@nginx.com { 3961759Svbart@nginx.com int ret; 3971759Svbart@nginx.com PyObject *path, *sys; 3981759Svbart@nginx.com nxt_str_t str; 3991759Svbart@nginx.com nxt_uint_t n; 4001759Svbart@nginx.com nxt_conf_value_t *array; 4011759Svbart@nginx.com 4021759Svbart@nginx.com if (value == NULL) { 4031759Svbart@nginx.com return NXT_OK; 4041759Svbart@nginx.com } 4051759Svbart@nginx.com 4061759Svbart@nginx.com sys = PySys_GetObject((char *) "path"); 4071759Svbart@nginx.com if (nxt_slow_path(sys == NULL)) { 4081759Svbart@nginx.com nxt_alert(task, "Python failed to get \"sys.path\" list"); 4091759Svbart@nginx.com return NXT_ERROR; 4101759Svbart@nginx.com } 4111759Svbart@nginx.com 4121759Svbart@nginx.com /* sys is a Borrowed reference. */ 4131759Svbart@nginx.com 4141759Svbart@nginx.com array = value; 415*2077Salx.manpages@gmail.com n = nxt_conf_array_elements_count_or_1(array); 4161759Svbart@nginx.com 4171759Svbart@nginx.com while (n != 0) { 4181759Svbart@nginx.com n--; 4191759Svbart@nginx.com 4201759Svbart@nginx.com /* 4211759Svbart@nginx.com * Insertion in front of existing paths starting from the last element 4221759Svbart@nginx.com * to preserve original order while giving priority to the values 4231759Svbart@nginx.com * specified in the "path" option. 4241759Svbart@nginx.com */ 4251759Svbart@nginx.com 426*2077Salx.manpages@gmail.com value = nxt_conf_get_array_element_or_itself(array, n); 4271759Svbart@nginx.com 4281759Svbart@nginx.com nxt_conf_get_string(value, &str); 4291759Svbart@nginx.com 4301759Svbart@nginx.com path = PyString_FromStringAndSize((char *) str.start, str.length); 4311759Svbart@nginx.com if (nxt_slow_path(path == NULL)) { 4321759Svbart@nginx.com nxt_alert(task, "Python failed to create string object \"%V\"", 4331759Svbart@nginx.com &str); 4341759Svbart@nginx.com return NXT_ERROR; 4351759Svbart@nginx.com } 4361759Svbart@nginx.com 4371759Svbart@nginx.com ret = PyList_Insert(sys, 0, path); 4381759Svbart@nginx.com 4391759Svbart@nginx.com Py_DECREF(path); 4401759Svbart@nginx.com 4411759Svbart@nginx.com if (nxt_slow_path(ret != 0)) { 4421759Svbart@nginx.com nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"", 4431759Svbart@nginx.com &str); 4441759Svbart@nginx.com return NXT_ERROR; 4451759Svbart@nginx.com } 4461759Svbart@nginx.com } 4471759Svbart@nginx.com 4481759Svbart@nginx.com return NXT_OK; 4491759Svbart@nginx.com } 4501759Svbart@nginx.com 4511759Svbart@nginx.com 4521681Smax.romanov@nginx.com static int 4531681Smax.romanov@nginx.com nxt_python_init_threads(nxt_python_app_conf_t *c) 4541681Smax.romanov@nginx.com { 4551681Smax.romanov@nginx.com int res; 4561681Smax.romanov@nginx.com uint32_t i; 4571681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 4581681Smax.romanov@nginx.com static pthread_attr_t attr; 4591681Smax.romanov@nginx.com 4601681Smax.romanov@nginx.com if (c->threads <= 1) { 4611681Smax.romanov@nginx.com return NXT_UNIT_OK; 4621681Smax.romanov@nginx.com } 4631681Smax.romanov@nginx.com 4641681Smax.romanov@nginx.com if (c->thread_stack_size > 0) { 4651681Smax.romanov@nginx.com res = pthread_attr_init(&attr); 4661681Smax.romanov@nginx.com if (nxt_slow_path(res != 0)) { 4671681Smax.romanov@nginx.com nxt_unit_alert(NULL, "thread attr init failed: %s (%d)", 4681681Smax.romanov@nginx.com strerror(res), res); 4691681Smax.romanov@nginx.com 4701681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4711681Smax.romanov@nginx.com } 4721681Smax.romanov@nginx.com 4731681Smax.romanov@nginx.com res = pthread_attr_setstacksize(&attr, c->thread_stack_size); 4741681Smax.romanov@nginx.com if (nxt_slow_path(res != 0)) { 4751681Smax.romanov@nginx.com nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)", 4761681Smax.romanov@nginx.com strerror(res), res); 4771681Smax.romanov@nginx.com 4781681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4791681Smax.romanov@nginx.com } 4801681Smax.romanov@nginx.com 4811681Smax.romanov@nginx.com nxt_py_thread_attr = &attr; 4821681Smax.romanov@nginx.com } 4831681Smax.romanov@nginx.com 4841681Smax.romanov@nginx.com nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t) 4851681Smax.romanov@nginx.com * (c->threads - 1)); 4861681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_threads == NULL)) { 4871681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate thread info array"); 4881681Smax.romanov@nginx.com 4891681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4901681Smax.romanov@nginx.com } 4911681Smax.romanov@nginx.com 4921681Smax.romanov@nginx.com memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1)); 4931681Smax.romanov@nginx.com 4941681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 4951681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 4961681Smax.romanov@nginx.com 4971918Smax.romanov@nginx.com res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0); 4981681Smax.romanov@nginx.com if (nxt_slow_path(res != NXT_UNIT_OK)) { 4991681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 5001681Smax.romanov@nginx.com } 5011681Smax.romanov@nginx.com } 5021681Smax.romanov@nginx.com 5031681Smax.romanov@nginx.com return NXT_UNIT_OK; 5041681Smax.romanov@nginx.com } 5051681Smax.romanov@nginx.com 5061681Smax.romanov@nginx.com 5071681Smax.romanov@nginx.com static int 5081681Smax.romanov@nginx.com nxt_python_ready_handler(nxt_unit_ctx_t *ctx) 5091681Smax.romanov@nginx.com { 5101681Smax.romanov@nginx.com int res; 5111681Smax.romanov@nginx.com uint32_t i; 5121681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 5131681Smax.romanov@nginx.com nxt_python_app_conf_t *c; 5141681Smax.romanov@nginx.com 5151681Smax.romanov@nginx.com c = ctx->unit->data; 5161681Smax.romanov@nginx.com 5171681Smax.romanov@nginx.com if (c->threads <= 1) { 5181681Smax.romanov@nginx.com return NXT_UNIT_OK; 5191681Smax.romanov@nginx.com } 5201681Smax.romanov@nginx.com 5211681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 5221681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 5231681Smax.romanov@nginx.com 5241681Smax.romanov@nginx.com ti->ctx = ctx; 5251681Smax.romanov@nginx.com 5261681Smax.romanov@nginx.com res = pthread_create(&ti->thread, nxt_py_thread_attr, 5271681Smax.romanov@nginx.com nxt_python_thread_func, ti); 5281681Smax.romanov@nginx.com 5291681Smax.romanov@nginx.com if (nxt_fast_path(res == 0)) { 5301681Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); 5311681Smax.romanov@nginx.com 5321681Smax.romanov@nginx.com } else { 5331681Smax.romanov@nginx.com nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)", 5341681Smax.romanov@nginx.com (int) (i + 1), strerror(res), res); 5351681Smax.romanov@nginx.com } 5361681Smax.romanov@nginx.com } 5371681Smax.romanov@nginx.com 5381681Smax.romanov@nginx.com return NXT_UNIT_OK; 5391681Smax.romanov@nginx.com } 5401681Smax.romanov@nginx.com 5411681Smax.romanov@nginx.com 5421681Smax.romanov@nginx.com static void * 5431681Smax.romanov@nginx.com nxt_python_thread_func(void *data) 5441681Smax.romanov@nginx.com { 5451681Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 5461681Smax.romanov@nginx.com PyGILState_STATE gstate; 5471681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 5481681Smax.romanov@nginx.com 5491681Smax.romanov@nginx.com ti = data; 5501681Smax.romanov@nginx.com 5511681Smax.romanov@nginx.com nxt_unit_debug(ti->ctx, "worker thread #%d start", 5521681Smax.romanov@nginx.com (int) (ti - nxt_py_threads + 1)); 5531681Smax.romanov@nginx.com 5541681Smax.romanov@nginx.com gstate = PyGILState_Ensure(); 5551681Smax.romanov@nginx.com 5561681Smax.romanov@nginx.com if (nxt_py_proto.startup != NULL) { 5571681Smax.romanov@nginx.com if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) { 5581681Smax.romanov@nginx.com goto fail; 5591681Smax.romanov@nginx.com } 5601681Smax.romanov@nginx.com } 5611681Smax.romanov@nginx.com 5621681Smax.romanov@nginx.com ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data); 5631681Smax.romanov@nginx.com if (nxt_slow_path(ctx == NULL)) { 5641681Smax.romanov@nginx.com goto fail; 5651681Smax.romanov@nginx.com } 5661681Smax.romanov@nginx.com 5671681Smax.romanov@nginx.com (void) nxt_py_proto.run(ctx); 5681681Smax.romanov@nginx.com 5691681Smax.romanov@nginx.com nxt_unit_done(ctx); 5701681Smax.romanov@nginx.com 5711681Smax.romanov@nginx.com fail: 5721681Smax.romanov@nginx.com 5731681Smax.romanov@nginx.com PyGILState_Release(gstate); 5741681Smax.romanov@nginx.com 5751681Smax.romanov@nginx.com nxt_unit_debug(NULL, "worker thread #%d end", 5761681Smax.romanov@nginx.com (int) (ti - nxt_py_threads + 1)); 5771681Smax.romanov@nginx.com 5781681Smax.romanov@nginx.com return NULL; 5791681Smax.romanov@nginx.com } 5801681Smax.romanov@nginx.com 5811681Smax.romanov@nginx.com 5821681Smax.romanov@nginx.com static void 5831681Smax.romanov@nginx.com nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c) 5841681Smax.romanov@nginx.com { 5851681Smax.romanov@nginx.com int res; 5861681Smax.romanov@nginx.com uint32_t i; 5871681Smax.romanov@nginx.com PyThreadState *thread_state; 5881681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 5891681Smax.romanov@nginx.com 5901681Smax.romanov@nginx.com if (nxt_py_threads == NULL) { 5911681Smax.romanov@nginx.com return; 5921681Smax.romanov@nginx.com } 5931681Smax.romanov@nginx.com 5941681Smax.romanov@nginx.com thread_state = PyEval_SaveThread(); 5951681Smax.romanov@nginx.com 5961681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 5971681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 5981681Smax.romanov@nginx.com 5991681Smax.romanov@nginx.com if ((uintptr_t) ti->thread == 0) { 6001681Smax.romanov@nginx.com continue; 6011681Smax.romanov@nginx.com } 6021681Smax.romanov@nginx.com 6031681Smax.romanov@nginx.com res = pthread_join(ti->thread, NULL); 6041681Smax.romanov@nginx.com 6051681Smax.romanov@nginx.com if (nxt_fast_path(res == 0)) { 6061681Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); 6071681Smax.romanov@nginx.com 6081681Smax.romanov@nginx.com } else { 6091681Smax.romanov@nginx.com nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)", 6101681Smax.romanov@nginx.com (int) (i + 1), strerror(res), res); 6111681Smax.romanov@nginx.com } 6121681Smax.romanov@nginx.com } 6131681Smax.romanov@nginx.com 6141681Smax.romanov@nginx.com PyEval_RestoreThread(thread_state); 6151681Smax.romanov@nginx.com 6161681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 6171681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 6181681Smax.romanov@nginx.com 6191681Smax.romanov@nginx.com if (ti->ctx_data != NULL) { 6201681Smax.romanov@nginx.com nxt_py_proto.ctx_data_free(ti->ctx_data); 6211681Smax.romanov@nginx.com } 6221681Smax.romanov@nginx.com } 6231681Smax.romanov@nginx.com 6241681Smax.romanov@nginx.com nxt_unit_free(NULL, nxt_py_threads); 6251681Smax.romanov@nginx.com } 6261681Smax.romanov@nginx.com 6271681Smax.romanov@nginx.com 6281681Smax.romanov@nginx.com int 6291592Smax.romanov@nginx.com nxt_python_init_strings(nxt_python_string_t *pstr) 6301592Smax.romanov@nginx.com { 6311592Smax.romanov@nginx.com PyObject *obj; 6321592Smax.romanov@nginx.com 6331592Smax.romanov@nginx.com while (pstr->string.start != NULL) { 6341592Smax.romanov@nginx.com obj = PyString_FromStringAndSize((char *) pstr->string.start, 6351592Smax.romanov@nginx.com pstr->string.length); 6361592Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 6371681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 6381592Smax.romanov@nginx.com } 6391592Smax.romanov@nginx.com 6401592Smax.romanov@nginx.com PyUnicode_InternInPlace(&obj); 6411592Smax.romanov@nginx.com 6421592Smax.romanov@nginx.com *pstr->object_p = obj; 6431592Smax.romanov@nginx.com 6441592Smax.romanov@nginx.com pstr++; 6451592Smax.romanov@nginx.com } 6461592Smax.romanov@nginx.com 6471681Smax.romanov@nginx.com return NXT_UNIT_OK; 6481592Smax.romanov@nginx.com } 6491592Smax.romanov@nginx.com 6501592Smax.romanov@nginx.com 6511592Smax.romanov@nginx.com void 6521592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_string_t *pstr) 6531592Smax.romanov@nginx.com { 6541592Smax.romanov@nginx.com PyObject *obj; 6551592Smax.romanov@nginx.com 6561592Smax.romanov@nginx.com while (pstr->string.start != NULL) { 6571592Smax.romanov@nginx.com obj = *pstr->object_p; 6581592Smax.romanov@nginx.com 6591592Smax.romanov@nginx.com Py_XDECREF(obj); 6601592Smax.romanov@nginx.com *pstr->object_p = NULL; 6611592Smax.romanov@nginx.com 6621592Smax.romanov@nginx.com pstr++; 6631592Smax.romanov@nginx.com } 6641592Smax.romanov@nginx.com } 6651592Smax.romanov@nginx.com 6661592Smax.romanov@nginx.com 6671592Smax.romanov@nginx.com static void 6681592Smax.romanov@nginx.com nxt_python_atexit(void) 6691592Smax.romanov@nginx.com { 6701872So.canty@f5.com nxt_int_t i; 6711872So.canty@f5.com 6721681Smax.romanov@nginx.com if (nxt_py_proto.done != NULL) { 6731681Smax.romanov@nginx.com nxt_py_proto.done(); 6741681Smax.romanov@nginx.com } 6751592Smax.romanov@nginx.com 6761592Smax.romanov@nginx.com Py_XDECREF(nxt_py_stderr_flush); 6771872So.canty@f5.com 6781872So.canty@f5.com if (nxt_py_targets != NULL) { 6791872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) { 6801872So.canty@f5.com Py_XDECREF(nxt_py_targets->target[i].application); 6811872So.canty@f5.com } 6821872So.canty@f5.com 6831872So.canty@f5.com nxt_unit_free(NULL, nxt_py_targets); 6841872So.canty@f5.com } 6851592Smax.romanov@nginx.com 6861592Smax.romanov@nginx.com Py_Finalize(); 6871592Smax.romanov@nginx.com 6881592Smax.romanov@nginx.com if (nxt_py_home != NULL) { 6891592Smax.romanov@nginx.com nxt_free(nxt_py_home); 6901592Smax.romanov@nginx.com } 6911592Smax.romanov@nginx.com } 6921592Smax.romanov@nginx.com 6931592Smax.romanov@nginx.com 6941592Smax.romanov@nginx.com void 6951592Smax.romanov@nginx.com nxt_python_print_exception(void) 6961592Smax.romanov@nginx.com { 6971592Smax.romanov@nginx.com PyErr_Print(); 6981592Smax.romanov@nginx.com 6991592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 7001592Smax.romanov@nginx.com /* The backtrace may be buffered in sys.stderr file object. */ 7011592Smax.romanov@nginx.com { 7021592Smax.romanov@nginx.com PyObject *result; 7031592Smax.romanov@nginx.com 7041592Smax.romanov@nginx.com result = PyObject_CallFunction(nxt_py_stderr_flush, NULL); 7051592Smax.romanov@nginx.com if (nxt_slow_path(result == NULL)) { 7061592Smax.romanov@nginx.com PyErr_Clear(); 7071592Smax.romanov@nginx.com return; 7081592Smax.romanov@nginx.com } 7091592Smax.romanov@nginx.com 7101592Smax.romanov@nginx.com Py_DECREF(result); 7111592Smax.romanov@nginx.com } 7121592Smax.romanov@nginx.com #endif 7131592Smax.romanov@nginx.com } 714