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 2331592Smax.romanov@nginx.com nxt_unit_default_init(task, &python_init); 2341592Smax.romanov@nginx.com 2351681Smax.romanov@nginx.com python_init.data = c; 2361592Smax.romanov@nginx.com python_init.shm_limit = data->app->shm_limit; 2371681Smax.romanov@nginx.com python_init.callbacks.ready_handler = nxt_python_ready_handler; 2381592Smax.romanov@nginx.com 2391697Smax.romanov@nginx.com proto = c->protocol; 2401697Smax.romanov@nginx.com 2411697Smax.romanov@nginx.com if (proto.length == 0) { 2421872So.canty@f5.com proto = nxt_python_asgi_check(targets->target[0].application) 2431872So.canty@f5.com ? asgi : wsgi; 2441872So.canty@f5.com 2451872So.canty@f5.com for (i = 1; i < targets->count; i++) { 2461872So.canty@f5.com probe_proto = nxt_python_asgi_check(targets->target[i].application) 2471872So.canty@f5.com ? asgi : wsgi; 2481872So.canty@f5.com if (probe_proto.start != proto.start) { 2491872So.canty@f5.com nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, " 2501872So.canty@f5.com "specify protocol in config if incorrect"); 2511872So.canty@f5.com goto fail; 2521872So.canty@f5.com } 2531872So.canty@f5.com } 2541697Smax.romanov@nginx.com } 2551697Smax.romanov@nginx.com 2561697Smax.romanov@nginx.com if (nxt_strstr_eq(&proto, &asgi)) { 2571681Smax.romanov@nginx.com rc = nxt_python_asgi_init(&python_init, &nxt_py_proto); 2581624Smax.romanov@nginx.com 2591624Smax.romanov@nginx.com } else { 2601681Smax.romanov@nginx.com rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto); 2611681Smax.romanov@nginx.com } 2621681Smax.romanov@nginx.com 2631681Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 2641681Smax.romanov@nginx.com goto fail; 2651624Smax.romanov@nginx.com } 2661624Smax.romanov@nginx.com 267*1918Smax.romanov@nginx.com rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1); 2681681Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 2691592Smax.romanov@nginx.com goto fail; 2701592Smax.romanov@nginx.com } 2711592Smax.romanov@nginx.com 2721681Smax.romanov@nginx.com rc = nxt_python_init_threads(c); 2731681Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 2741681Smax.romanov@nginx.com goto fail; 2751681Smax.romanov@nginx.com } 2761681Smax.romanov@nginx.com 2771681Smax.romanov@nginx.com if (nxt_py_proto.startup != NULL) { 2781681Smax.romanov@nginx.com if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) { 2791681Smax.romanov@nginx.com goto fail; 2801681Smax.romanov@nginx.com } 2811681Smax.romanov@nginx.com } 2821681Smax.romanov@nginx.com 2831592Smax.romanov@nginx.com unit_ctx = nxt_unit_init(&python_init); 2841592Smax.romanov@nginx.com if (nxt_slow_path(unit_ctx == NULL)) { 2851592Smax.romanov@nginx.com goto fail; 2861592Smax.romanov@nginx.com } 2871592Smax.romanov@nginx.com 2881681Smax.romanov@nginx.com rc = nxt_py_proto.run(unit_ctx); 2891624Smax.romanov@nginx.com 2901681Smax.romanov@nginx.com nxt_python_join_threads(unit_ctx, c); 2911592Smax.romanov@nginx.com 2921592Smax.romanov@nginx.com nxt_unit_done(unit_ctx); 2931592Smax.romanov@nginx.com 2941681Smax.romanov@nginx.com nxt_py_proto.ctx_data_free(python_init.ctx_data); 2951681Smax.romanov@nginx.com 2961592Smax.romanov@nginx.com nxt_python_atexit(); 2971592Smax.romanov@nginx.com 2981592Smax.romanov@nginx.com exit(rc); 2991592Smax.romanov@nginx.com 3001592Smax.romanov@nginx.com return NXT_OK; 3011592Smax.romanov@nginx.com 3021592Smax.romanov@nginx.com fail: 3031592Smax.romanov@nginx.com 3041681Smax.romanov@nginx.com nxt_python_join_threads(NULL, c); 3051681Smax.romanov@nginx.com 3061681Smax.romanov@nginx.com if (python_init.ctx_data != NULL) { 3071681Smax.romanov@nginx.com nxt_py_proto.ctx_data_free(python_init.ctx_data); 3081681Smax.romanov@nginx.com } 3091681Smax.romanov@nginx.com 3101592Smax.romanov@nginx.com Py_XDECREF(obj); 3111592Smax.romanov@nginx.com Py_XDECREF(module); 3121592Smax.romanov@nginx.com 3131592Smax.romanov@nginx.com nxt_python_atexit(); 3141592Smax.romanov@nginx.com 3151592Smax.romanov@nginx.com return NXT_ERROR; 3161592Smax.romanov@nginx.com } 3171592Smax.romanov@nginx.com 3181592Smax.romanov@nginx.com 3191759Svbart@nginx.com static nxt_int_t 3201872So.canty@f5.com nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, 3211872So.canty@f5.com nxt_conf_value_t *conf) 3221872So.canty@f5.com { 3231872So.canty@f5.com char *callable, *module_name; 3241872So.canty@f5.com PyObject *module, *obj; 3251872So.canty@f5.com nxt_str_t str; 3261872So.canty@f5.com nxt_conf_value_t *value; 3271872So.canty@f5.com 3281872So.canty@f5.com static nxt_str_t module_str = nxt_string("module"); 3291872So.canty@f5.com static nxt_str_t callable_str = nxt_string("callable"); 3301872So.canty@f5.com 3311872So.canty@f5.com module = obj = NULL; 3321872So.canty@f5.com 3331872So.canty@f5.com value = nxt_conf_get_object_member(conf, &module_str, NULL); 3341872So.canty@f5.com if (nxt_slow_path(value == NULL)) { 3351872So.canty@f5.com goto fail; 3361872So.canty@f5.com } 3371872So.canty@f5.com 3381872So.canty@f5.com nxt_conf_get_string(value, &str); 3391872So.canty@f5.com 3401872So.canty@f5.com module_name = nxt_alloca(str.length + 1); 3411872So.canty@f5.com nxt_memcpy(module_name, str.start, str.length); 3421872So.canty@f5.com module_name[str.length] = '\0'; 3431872So.canty@f5.com 3441872So.canty@f5.com module = PyImport_ImportModule(module_name); 3451872So.canty@f5.com if (nxt_slow_path(module == NULL)) { 3461872So.canty@f5.com nxt_alert(task, "Python failed to import module \"%s\"", module_name); 3471872So.canty@f5.com nxt_python_print_exception(); 3481872So.canty@f5.com goto fail; 3491872So.canty@f5.com } 3501872So.canty@f5.com 3511872So.canty@f5.com value = nxt_conf_get_object_member(conf, &callable_str, NULL); 3521872So.canty@f5.com if (value == NULL) { 3531872So.canty@f5.com callable = nxt_alloca(12); 3541872So.canty@f5.com nxt_memcpy(callable, "application", 12); 3551872So.canty@f5.com 3561872So.canty@f5.com } else { 3571872So.canty@f5.com nxt_conf_get_string(value, &str); 3581872So.canty@f5.com 3591872So.canty@f5.com callable = nxt_alloca(str.length + 1); 3601872So.canty@f5.com nxt_memcpy(callable, str.start, str.length); 3611872So.canty@f5.com callable[str.length] = '\0'; 3621872So.canty@f5.com } 3631872So.canty@f5.com 3641872So.canty@f5.com obj = PyDict_GetItemString(PyModule_GetDict(module), callable); 3651872So.canty@f5.com if (nxt_slow_path(obj == NULL)) { 3661872So.canty@f5.com nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"", 3671872So.canty@f5.com callable, module); 3681872So.canty@f5.com goto fail; 3691872So.canty@f5.com } 3701872So.canty@f5.com 3711872So.canty@f5.com if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 3721872So.canty@f5.com nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object", 3731872So.canty@f5.com callable, module); 3741872So.canty@f5.com goto fail; 3751872So.canty@f5.com } 3761872So.canty@f5.com 3771872So.canty@f5.com target->application = obj; 3781872So.canty@f5.com obj = NULL; 3791872So.canty@f5.com 3801872So.canty@f5.com Py_INCREF(target->application); 3811872So.canty@f5.com Py_CLEAR(module); 3821872So.canty@f5.com 3831872So.canty@f5.com return NXT_OK; 3841872So.canty@f5.com 3851872So.canty@f5.com fail: 3861872So.canty@f5.com 3871872So.canty@f5.com Py_XDECREF(obj); 3881872So.canty@f5.com Py_XDECREF(module); 3891872So.canty@f5.com 3901872So.canty@f5.com return NXT_ERROR; 3911872So.canty@f5.com } 3921872So.canty@f5.com 3931872So.canty@f5.com 3941872So.canty@f5.com static nxt_int_t 3951759Svbart@nginx.com nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value) 3961759Svbart@nginx.com { 3971759Svbart@nginx.com int ret; 3981759Svbart@nginx.com PyObject *path, *sys; 3991759Svbart@nginx.com nxt_str_t str; 4001759Svbart@nginx.com nxt_uint_t n; 4011759Svbart@nginx.com nxt_conf_value_t *array; 4021759Svbart@nginx.com 4031759Svbart@nginx.com if (value == NULL) { 4041759Svbart@nginx.com return NXT_OK; 4051759Svbart@nginx.com } 4061759Svbart@nginx.com 4071759Svbart@nginx.com sys = PySys_GetObject((char *) "path"); 4081759Svbart@nginx.com if (nxt_slow_path(sys == NULL)) { 4091759Svbart@nginx.com nxt_alert(task, "Python failed to get \"sys.path\" list"); 4101759Svbart@nginx.com return NXT_ERROR; 4111759Svbart@nginx.com } 4121759Svbart@nginx.com 4131759Svbart@nginx.com /* sys is a Borrowed reference. */ 4141759Svbart@nginx.com 4151759Svbart@nginx.com if (nxt_conf_type(value) == NXT_CONF_STRING) { 4161759Svbart@nginx.com n = 0; 4171759Svbart@nginx.com goto value_is_string; 4181759Svbart@nginx.com } 4191759Svbart@nginx.com 4201759Svbart@nginx.com /* NXT_CONF_ARRAY */ 4211759Svbart@nginx.com array = value; 4221759Svbart@nginx.com 4231759Svbart@nginx.com n = nxt_conf_array_elements_count(array); 4241759Svbart@nginx.com 4251759Svbart@nginx.com while (n != 0) { 4261759Svbart@nginx.com n--; 4271759Svbart@nginx.com 4281759Svbart@nginx.com /* 4291759Svbart@nginx.com * Insertion in front of existing paths starting from the last element 4301759Svbart@nginx.com * to preserve original order while giving priority to the values 4311759Svbart@nginx.com * specified in the "path" option. 4321759Svbart@nginx.com */ 4331759Svbart@nginx.com 4341759Svbart@nginx.com value = nxt_conf_get_array_element(array, n); 4351759Svbart@nginx.com 4361759Svbart@nginx.com value_is_string: 4371759Svbart@nginx.com 4381759Svbart@nginx.com nxt_conf_get_string(value, &str); 4391759Svbart@nginx.com 4401759Svbart@nginx.com path = PyString_FromStringAndSize((char *) str.start, str.length); 4411759Svbart@nginx.com if (nxt_slow_path(path == NULL)) { 4421759Svbart@nginx.com nxt_alert(task, "Python failed to create string object \"%V\"", 4431759Svbart@nginx.com &str); 4441759Svbart@nginx.com return NXT_ERROR; 4451759Svbart@nginx.com } 4461759Svbart@nginx.com 4471759Svbart@nginx.com ret = PyList_Insert(sys, 0, path); 4481759Svbart@nginx.com 4491759Svbart@nginx.com Py_DECREF(path); 4501759Svbart@nginx.com 4511759Svbart@nginx.com if (nxt_slow_path(ret != 0)) { 4521759Svbart@nginx.com nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"", 4531759Svbart@nginx.com &str); 4541759Svbart@nginx.com return NXT_ERROR; 4551759Svbart@nginx.com } 4561759Svbart@nginx.com } 4571759Svbart@nginx.com 4581759Svbart@nginx.com return NXT_OK; 4591759Svbart@nginx.com } 4601759Svbart@nginx.com 4611759Svbart@nginx.com 4621681Smax.romanov@nginx.com static int 4631681Smax.romanov@nginx.com nxt_python_init_threads(nxt_python_app_conf_t *c) 4641681Smax.romanov@nginx.com { 4651681Smax.romanov@nginx.com int res; 4661681Smax.romanov@nginx.com uint32_t i; 4671681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 4681681Smax.romanov@nginx.com static pthread_attr_t attr; 4691681Smax.romanov@nginx.com 4701681Smax.romanov@nginx.com if (c->threads <= 1) { 4711681Smax.romanov@nginx.com return NXT_UNIT_OK; 4721681Smax.romanov@nginx.com } 4731681Smax.romanov@nginx.com 4741681Smax.romanov@nginx.com if (c->thread_stack_size > 0) { 4751681Smax.romanov@nginx.com res = pthread_attr_init(&attr); 4761681Smax.romanov@nginx.com if (nxt_slow_path(res != 0)) { 4771681Smax.romanov@nginx.com nxt_unit_alert(NULL, "thread attr init failed: %s (%d)", 4781681Smax.romanov@nginx.com strerror(res), res); 4791681Smax.romanov@nginx.com 4801681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4811681Smax.romanov@nginx.com } 4821681Smax.romanov@nginx.com 4831681Smax.romanov@nginx.com res = pthread_attr_setstacksize(&attr, c->thread_stack_size); 4841681Smax.romanov@nginx.com if (nxt_slow_path(res != 0)) { 4851681Smax.romanov@nginx.com nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)", 4861681Smax.romanov@nginx.com strerror(res), res); 4871681Smax.romanov@nginx.com 4881681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4891681Smax.romanov@nginx.com } 4901681Smax.romanov@nginx.com 4911681Smax.romanov@nginx.com nxt_py_thread_attr = &attr; 4921681Smax.romanov@nginx.com } 4931681Smax.romanov@nginx.com 4941681Smax.romanov@nginx.com nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t) 4951681Smax.romanov@nginx.com * (c->threads - 1)); 4961681Smax.romanov@nginx.com if (nxt_slow_path(nxt_py_threads == NULL)) { 4971681Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate thread info array"); 4981681Smax.romanov@nginx.com 4991681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 5001681Smax.romanov@nginx.com } 5011681Smax.romanov@nginx.com 5021681Smax.romanov@nginx.com memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1)); 5031681Smax.romanov@nginx.com 5041681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 5051681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 5061681Smax.romanov@nginx.com 507*1918Smax.romanov@nginx.com res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0); 5081681Smax.romanov@nginx.com if (nxt_slow_path(res != NXT_UNIT_OK)) { 5091681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 5101681Smax.romanov@nginx.com } 5111681Smax.romanov@nginx.com } 5121681Smax.romanov@nginx.com 5131681Smax.romanov@nginx.com return NXT_UNIT_OK; 5141681Smax.romanov@nginx.com } 5151681Smax.romanov@nginx.com 5161681Smax.romanov@nginx.com 5171681Smax.romanov@nginx.com static int 5181681Smax.romanov@nginx.com nxt_python_ready_handler(nxt_unit_ctx_t *ctx) 5191681Smax.romanov@nginx.com { 5201681Smax.romanov@nginx.com int res; 5211681Smax.romanov@nginx.com uint32_t i; 5221681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 5231681Smax.romanov@nginx.com nxt_python_app_conf_t *c; 5241681Smax.romanov@nginx.com 5251681Smax.romanov@nginx.com if (nxt_py_proto.ready != NULL) { 5261681Smax.romanov@nginx.com res = nxt_py_proto.ready(ctx); 5271681Smax.romanov@nginx.com if (nxt_slow_path(res != NXT_UNIT_OK)) { 5281681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 5291681Smax.romanov@nginx.com } 5301681Smax.romanov@nginx.com } 5311681Smax.romanov@nginx.com 5321681Smax.romanov@nginx.com /* Worker thread context. */ 5331681Smax.romanov@nginx.com if (!nxt_unit_is_main_ctx(ctx)) { 5341681Smax.romanov@nginx.com return NXT_UNIT_OK; 5351681Smax.romanov@nginx.com } 5361681Smax.romanov@nginx.com 5371681Smax.romanov@nginx.com c = ctx->unit->data; 5381681Smax.romanov@nginx.com 5391681Smax.romanov@nginx.com if (c->threads <= 1) { 5401681Smax.romanov@nginx.com return NXT_UNIT_OK; 5411681Smax.romanov@nginx.com } 5421681Smax.romanov@nginx.com 5431681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 5441681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 5451681Smax.romanov@nginx.com 5461681Smax.romanov@nginx.com ti->ctx = ctx; 5471681Smax.romanov@nginx.com 5481681Smax.romanov@nginx.com res = pthread_create(&ti->thread, nxt_py_thread_attr, 5491681Smax.romanov@nginx.com nxt_python_thread_func, ti); 5501681Smax.romanov@nginx.com 5511681Smax.romanov@nginx.com if (nxt_fast_path(res == 0)) { 5521681Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); 5531681Smax.romanov@nginx.com 5541681Smax.romanov@nginx.com } else { 5551681Smax.romanov@nginx.com nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)", 5561681Smax.romanov@nginx.com (int) (i + 1), strerror(res), res); 5571681Smax.romanov@nginx.com } 5581681Smax.romanov@nginx.com } 5591681Smax.romanov@nginx.com 5601681Smax.romanov@nginx.com return NXT_UNIT_OK; 5611681Smax.romanov@nginx.com } 5621681Smax.romanov@nginx.com 5631681Smax.romanov@nginx.com 5641681Smax.romanov@nginx.com static void * 5651681Smax.romanov@nginx.com nxt_python_thread_func(void *data) 5661681Smax.romanov@nginx.com { 5671681Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 5681681Smax.romanov@nginx.com PyGILState_STATE gstate; 5691681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 5701681Smax.romanov@nginx.com 5711681Smax.romanov@nginx.com ti = data; 5721681Smax.romanov@nginx.com 5731681Smax.romanov@nginx.com nxt_unit_debug(ti->ctx, "worker thread #%d start", 5741681Smax.romanov@nginx.com (int) (ti - nxt_py_threads + 1)); 5751681Smax.romanov@nginx.com 5761681Smax.romanov@nginx.com gstate = PyGILState_Ensure(); 5771681Smax.romanov@nginx.com 5781681Smax.romanov@nginx.com if (nxt_py_proto.startup != NULL) { 5791681Smax.romanov@nginx.com if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) { 5801681Smax.romanov@nginx.com goto fail; 5811681Smax.romanov@nginx.com } 5821681Smax.romanov@nginx.com } 5831681Smax.romanov@nginx.com 5841681Smax.romanov@nginx.com ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data); 5851681Smax.romanov@nginx.com if (nxt_slow_path(ctx == NULL)) { 5861681Smax.romanov@nginx.com goto fail; 5871681Smax.romanov@nginx.com } 5881681Smax.romanov@nginx.com 5891681Smax.romanov@nginx.com (void) nxt_py_proto.run(ctx); 5901681Smax.romanov@nginx.com 5911681Smax.romanov@nginx.com nxt_unit_done(ctx); 5921681Smax.romanov@nginx.com 5931681Smax.romanov@nginx.com fail: 5941681Smax.romanov@nginx.com 5951681Smax.romanov@nginx.com PyGILState_Release(gstate); 5961681Smax.romanov@nginx.com 5971681Smax.romanov@nginx.com nxt_unit_debug(NULL, "worker thread #%d end", 5981681Smax.romanov@nginx.com (int) (ti - nxt_py_threads + 1)); 5991681Smax.romanov@nginx.com 6001681Smax.romanov@nginx.com return NULL; 6011681Smax.romanov@nginx.com } 6021681Smax.romanov@nginx.com 6031681Smax.romanov@nginx.com 6041681Smax.romanov@nginx.com static void 6051681Smax.romanov@nginx.com nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c) 6061681Smax.romanov@nginx.com { 6071681Smax.romanov@nginx.com int res; 6081681Smax.romanov@nginx.com uint32_t i; 6091681Smax.romanov@nginx.com PyThreadState *thread_state; 6101681Smax.romanov@nginx.com nxt_py_thread_info_t *ti; 6111681Smax.romanov@nginx.com 6121681Smax.romanov@nginx.com if (nxt_py_threads == NULL) { 6131681Smax.romanov@nginx.com return; 6141681Smax.romanov@nginx.com } 6151681Smax.romanov@nginx.com 6161681Smax.romanov@nginx.com thread_state = PyEval_SaveThread(); 6171681Smax.romanov@nginx.com 6181681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 6191681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 6201681Smax.romanov@nginx.com 6211681Smax.romanov@nginx.com if ((uintptr_t) ti->thread == 0) { 6221681Smax.romanov@nginx.com continue; 6231681Smax.romanov@nginx.com } 6241681Smax.romanov@nginx.com 6251681Smax.romanov@nginx.com res = pthread_join(ti->thread, NULL); 6261681Smax.romanov@nginx.com 6271681Smax.romanov@nginx.com if (nxt_fast_path(res == 0)) { 6281681Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); 6291681Smax.romanov@nginx.com 6301681Smax.romanov@nginx.com } else { 6311681Smax.romanov@nginx.com nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)", 6321681Smax.romanov@nginx.com (int) (i + 1), strerror(res), res); 6331681Smax.romanov@nginx.com } 6341681Smax.romanov@nginx.com } 6351681Smax.romanov@nginx.com 6361681Smax.romanov@nginx.com PyEval_RestoreThread(thread_state); 6371681Smax.romanov@nginx.com 6381681Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 6391681Smax.romanov@nginx.com ti = &nxt_py_threads[i]; 6401681Smax.romanov@nginx.com 6411681Smax.romanov@nginx.com if (ti->ctx_data != NULL) { 6421681Smax.romanov@nginx.com nxt_py_proto.ctx_data_free(ti->ctx_data); 6431681Smax.romanov@nginx.com } 6441681Smax.romanov@nginx.com } 6451681Smax.romanov@nginx.com 6461681Smax.romanov@nginx.com nxt_unit_free(NULL, nxt_py_threads); 6471681Smax.romanov@nginx.com } 6481681Smax.romanov@nginx.com 6491681Smax.romanov@nginx.com 6501681Smax.romanov@nginx.com int 6511592Smax.romanov@nginx.com nxt_python_init_strings(nxt_python_string_t *pstr) 6521592Smax.romanov@nginx.com { 6531592Smax.romanov@nginx.com PyObject *obj; 6541592Smax.romanov@nginx.com 6551592Smax.romanov@nginx.com while (pstr->string.start != NULL) { 6561592Smax.romanov@nginx.com obj = PyString_FromStringAndSize((char *) pstr->string.start, 6571592Smax.romanov@nginx.com pstr->string.length); 6581592Smax.romanov@nginx.com if (nxt_slow_path(obj == NULL)) { 6591681Smax.romanov@nginx.com return NXT_UNIT_ERROR; 6601592Smax.romanov@nginx.com } 6611592Smax.romanov@nginx.com 6621592Smax.romanov@nginx.com PyUnicode_InternInPlace(&obj); 6631592Smax.romanov@nginx.com 6641592Smax.romanov@nginx.com *pstr->object_p = obj; 6651592Smax.romanov@nginx.com 6661592Smax.romanov@nginx.com pstr++; 6671592Smax.romanov@nginx.com } 6681592Smax.romanov@nginx.com 6691681Smax.romanov@nginx.com return NXT_UNIT_OK; 6701592Smax.romanov@nginx.com } 6711592Smax.romanov@nginx.com 6721592Smax.romanov@nginx.com 6731592Smax.romanov@nginx.com void 6741592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_string_t *pstr) 6751592Smax.romanov@nginx.com { 6761592Smax.romanov@nginx.com PyObject *obj; 6771592Smax.romanov@nginx.com 6781592Smax.romanov@nginx.com while (pstr->string.start != NULL) { 6791592Smax.romanov@nginx.com obj = *pstr->object_p; 6801592Smax.romanov@nginx.com 6811592Smax.romanov@nginx.com Py_XDECREF(obj); 6821592Smax.romanov@nginx.com *pstr->object_p = NULL; 6831592Smax.romanov@nginx.com 6841592Smax.romanov@nginx.com pstr++; 6851592Smax.romanov@nginx.com } 6861592Smax.romanov@nginx.com } 6871592Smax.romanov@nginx.com 6881592Smax.romanov@nginx.com 6891592Smax.romanov@nginx.com static void 6901592Smax.romanov@nginx.com nxt_python_atexit(void) 6911592Smax.romanov@nginx.com { 6921872So.canty@f5.com nxt_int_t i; 6931872So.canty@f5.com 6941681Smax.romanov@nginx.com if (nxt_py_proto.done != NULL) { 6951681Smax.romanov@nginx.com nxt_py_proto.done(); 6961681Smax.romanov@nginx.com } 6971592Smax.romanov@nginx.com 6981592Smax.romanov@nginx.com Py_XDECREF(nxt_py_stderr_flush); 6991872So.canty@f5.com 7001872So.canty@f5.com if (nxt_py_targets != NULL) { 7011872So.canty@f5.com for (i = 0; i < nxt_py_targets->count; i++) { 7021872So.canty@f5.com Py_XDECREF(nxt_py_targets->target[i].application); 7031872So.canty@f5.com } 7041872So.canty@f5.com 7051872So.canty@f5.com nxt_unit_free(NULL, nxt_py_targets); 7061872So.canty@f5.com } 7071592Smax.romanov@nginx.com 7081592Smax.romanov@nginx.com Py_Finalize(); 7091592Smax.romanov@nginx.com 7101592Smax.romanov@nginx.com if (nxt_py_home != NULL) { 7111592Smax.romanov@nginx.com nxt_free(nxt_py_home); 7121592Smax.romanov@nginx.com } 7131592Smax.romanov@nginx.com } 7141592Smax.romanov@nginx.com 7151592Smax.romanov@nginx.com 7161592Smax.romanov@nginx.com void 7171592Smax.romanov@nginx.com nxt_python_print_exception(void) 7181592Smax.romanov@nginx.com { 7191592Smax.romanov@nginx.com PyErr_Print(); 7201592Smax.romanov@nginx.com 7211592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3 7221592Smax.romanov@nginx.com /* The backtrace may be buffered in sys.stderr file object. */ 7231592Smax.romanov@nginx.com { 7241592Smax.romanov@nginx.com PyObject *result; 7251592Smax.romanov@nginx.com 7261592Smax.romanov@nginx.com result = PyObject_CallFunction(nxt_py_stderr_flush, NULL); 7271592Smax.romanov@nginx.com if (nxt_slow_path(result == NULL)) { 7281592Smax.romanov@nginx.com PyErr_Clear(); 7291592Smax.romanov@nginx.com return; 7301592Smax.romanov@nginx.com } 7311592Smax.romanov@nginx.com 7321592Smax.romanov@nginx.com Py_DECREF(result); 7331592Smax.romanov@nginx.com } 7341592Smax.romanov@nginx.com #endif 7351592Smax.romanov@nginx.com } 736