xref: /unit/src/python/nxt_python.c (revision 1592)
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