Browse Source

Add pyutils.h/c and a check for import when parsing arguments

Yann Weber 4 years ago
parent
commit
3a9238e7a1
7 changed files with 281 additions and 203 deletions
  1. 5
    0
      include/conf.h
  2. 48
    0
      include/pyutils.h
  3. 2
    13
      include/pyworker.h
  4. 1
    1
      src/Makefile.am
  5. 27
    0
      src/conf.c
  6. 198
    0
      src/pyutils.c
  7. 0
    189
      src/pyworker.c

+ 5
- 0
include/conf.h View File

@@ -21,6 +21,8 @@
21 21
 
22 22
 #include <unistd.h>
23 23
 #include <getopt.h>
24
+#include <sys/types.h>
25
+#include <sys/wait.h>
24 26
 
25 27
 #include "config.h"
26 28
 /**@defgroup conf_internal PYFCGI configuration handling
@@ -30,6 +32,7 @@
30 32
  * @ingroup conf_internal */
31 33
 
32 34
 #include "logger.h"
35
+#include "pyutils.h"
33 36
 
34 37
 /**@defgroup ret_status Function & process return status
35 38
  */
@@ -152,6 +155,8 @@ void default_conf();
152 155
  * @return 0 if no error */
153 156
 int parse_args(int argc, char *argv[]);
154 157
 
158
+int check_entrypoint_import();
159
+
155 160
 int parse_optlog(const char*);
156 161
 
157 162
 #endif

+ 48
- 0
include/pyutils.h View File

@@ -0,0 +1,48 @@
1
+/*
2
+ * Copyright (C) 2019 Weber Yann
3
+ * 
4
+ * This file is part of PyFCGI.
5
+ * 
6
+ * PyFCGI is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * PyFCGI is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU Affero General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU Affero General Public License
17
+ * along with PyFCGI.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef _PYUTILS__H__
20
+#define _PYUTILS__H__
21
+
22
+#include "config.h"
23
+
24
+#include <fcgiapp.h>
25
+#include <fcgi_stdio.h> /* fcgi library; put it first*/
26
+
27
+#define PY_SSIZE_T_CLEAN
28
+#include <Python.h>
29
+
30
+#include "logger.h"
31
+
32
+/**@brief Call Py_Initialize & update_python_path
33
+ */
34
+void pyinit();
35
+
36
+/**@brief Add . to the embed pythonpath
37
+ */
38
+void update_python_path();
39
+
40
+/**@brief Import & return the python entrypoint callable
41
+ * from PyFCGI_conf.py_entrymod & PyFCGI_conf.py_entryfun
42
+ */
43
+PyObject* import_entrypoint();
44
+
45
+void log_expt(int priority);
46
+
47
+#endif
48
+

+ 2
- 13
include/pyworker.h View File

@@ -79,10 +79,10 @@
79 79
 #include <sys/wait.h>
80 80
 
81 81
 #include "logger.h"
82
+#include "pyutils.h"
82 83
 
83 84
 #define EXIT_PYERR 42
84 85
 #define WPIPER_SIG 30
85
-#define PYENTRY_FUNNAME "entrypoint"
86 86
 #define PIPER_STACK_SZ (1024 * 1024 * 4)
87 87
 
88 88
 //extern char **environ;
@@ -138,21 +138,12 @@ void worker_piper_sighandler(int);
138 138
  */
139 139
 int ctl_get_rep_sz(int, size_t*);
140 140
 
141
-/**@brief Import & return the python entrypoint callable
142
- * from PyFCGI_conf.py_entrymod & PyFCGI_conf.py_entryfun
143
- */
144
-PyObject* import_entrypoint();
145
-
146 141
 /**@brief Fetch stdout & stderr python flush() function
147 142
  * @param PyObject* pystdout_flush
148 143
  * @param PyObject* pystderr_flush
149 144
  */
150 145
 void fetch_pyflush(PyObject**, PyObject**);
151 146
 
152
-/**@brief Add . to the embed pythonpath
153
- */
154
-void update_python_path();
155
-
156 147
 /**@brief Create two pipes for stdout & stderr
157 148
  * @ingroup worker_process
158 149
  * @params int[2] pystdout
@@ -170,7 +161,5 @@ void update_python_fd(int[2], int[2]);
170 161
  */
171 162
 void update_pyenv(PyObject*, char**);
172 163
 
173
-
174
-void log_expt(int priority);
175
-
176 164
 #endif
165
+

+ 1
- 1
src/Makefile.am View File

@@ -1,7 +1,7 @@
1 1
 bin_PROGRAMS = pyfcgi
2 2
 noinst_LIBRARIES = libpyfcgi.a
3 3
 
4
-libpyfcgi_a_SOURCES = logger.c pyworker.c responder.c conf.c
4
+libpyfcgi_a_SOURCES = logger.c pyworker.c responder.c conf.c pyutils.c
5 5
 libpyfcgi_a_CFLAGS = $(PYTHON_CFLAGS)
6 6
 
7 7
 

+ 27
- 0
src/conf.c View File

@@ -123,9 +123,36 @@ int parse_args(int argc, char *argv[])
123 123
 	{
124 124
 		PyFCGI_conf.py_entryfun = PYENTRY_DEFAULT_FUN;
125 125
 	}
126
+	if(check_entrypoint_import())
127
+	{
128
+		usage();
129
+		exit(3);
130
+	}
126 131
 	return 0;
127 132
 }
128 133
 
134
+int check_entrypoint_import()
135
+{
136
+	pid_t pid;
137
+	int status;
138
+	void *ret;
139
+
140
+	pid = fork();
141
+	if(!pid)
142
+	{
143
+		pyinit();
144
+		ret = (void*)import_entrypoint();
145
+		if(!ret)
146
+		{
147
+			dprintf(2, "Unable to import entrypoint...\n");
148
+			exit(1);
149
+		}
150
+		exit(0);
151
+	}
152
+	waitpid(pid, &status, 0);
153
+	return WEXITSTATUS(status);
154
+}
155
+
129 156
 int parse_optlog(const char* logspec)
130 157
 {
131 158
 	return 0;

+ 198
- 0
src/pyutils.c View File

@@ -0,0 +1,198 @@
1
+#include "pyutils.h"
2
+
3
+void pyinit()
4
+{
5
+	update_python_path();
6
+	Py_Initialize();
7
+}
8
+
9
+void update_python_path()
10
+{
11
+	wchar_t *ppath, *wcwd, *wtmp;
12
+	char *cwd, *ncwd, *err_fmt;
13
+	size_t wcwd_sz, ppath_sz;
14
+	int err;
15
+	
16
+	// Add a ':' in front of CWD and convert to wchar_t*
17
+	cwd = get_current_dir_name();
18
+	if(!cwd)
19
+	{
20
+		err = errno;
21
+		err_fmt = "Unable to fecth CWD : %s";
22
+		goto update_python_path_err;
23
+	}
24
+	ncwd = malloc(sizeof(char) * (strlen(cwd) + 2));
25
+	if(!ncwd)
26
+	{
27
+		err = errno;
28
+		err_fmt = "Unable to allocate memory for new CWD : %s";
29
+		goto update_python_path_err_cwd;
30
+	}
31
+	ncwd[0] = ':';
32
+	strcpy(ncwd+1, cwd);
33
+	free(cwd);
34
+	cwd = ncwd;
35
+
36
+	wcwd_sz = mbstowcs(NULL, cwd, 0);
37
+	if(wcwd_sz == (size_t)-1)
38
+	{
39
+		err = errno;
40
+		err_fmt = "Unable to convert CWD to wchar : %s";
41
+		goto update_python_path_err_cwd;
42
+	}
43
+	wcwd_sz++;
44
+	wcwd = malloc(sizeof(wchar_t) * wcwd_sz);
45
+	if(!wcwd)
46
+	{
47
+		err = errno;
48
+		err_fmt = "Unable to allocate wchar for CWD : %s";
49
+		goto update_python_path_err_cwd;
50
+
51
+	}
52
+	if(mbstowcs(wcwd, cwd, wcwd_sz) == -1)
53
+	{
54
+		err = errno;
55
+		err_fmt = "Unable to convert CWD to wchar : %s";
56
+		goto update_python_path_err_wcwd;
57
+	}
58
+
59
+	// Fetch, dup & update the python path
60
+	ppath = Py_GetPath();
61
+	if(!ppath)
62
+	{
63
+		if(PyErr_Occurred())
64
+		{
65
+			log_expt(LOG_ALERT);
66
+		}
67
+		else
68
+		{
69
+			pyfcgi_log(LOG_ALERT, "Unable to fetch python path, got NULL.");
70
+		}
71
+		err_fmt = NULL;
72
+		err = 0;
73
+		goto update_python_path_err_wcwd;
74
+	}
75
+	ppath = wcsdup(ppath);
76
+	if(!wcwd)
77
+	{
78
+		err = errno;
79
+		err_fmt = "Unable to dup the python PATH : %s";
80
+		goto update_python_path_err_wcwd;
81
+	}
82
+
83
+	ppath_sz = wcslen(ppath);
84
+	wtmp = realloc(ppath, sizeof(wchar_t) * (ppath_sz + wcwd_sz + 1));
85
+	if(!wtmp)
86
+	{
87
+		err = errno;
88
+		err_fmt = "Unable reallocate new python path : %s";
89
+		goto update_python_path_err_ppath;
90
+	}
91
+	ppath = wtmp;
92
+	wcsncpy(ppath+ppath_sz, wcwd, wcwd_sz);
93
+	Py_SetPath(ppath);
94
+
95
+	free(ppath);
96
+	free(wcwd);
97
+	free(cwd);
98
+
99
+	return;
100
+	
101
+update_python_path_err_ppath:
102
+	free(ppath);
103
+update_python_path_err_wcwd:
104
+	free(wcwd);
105
+update_python_path_err_cwd:
106
+	free(cwd);
107
+update_python_path_err:
108
+	if(err_fmt)
109
+	{
110
+		pyfcgi_log( LOG_ALERT, err_fmt, strerror(err));
111
+	}
112
+	exit(err);
113
+}
114
+
115
+PyObject* import_entrypoint()
116
+{
117
+	PyObject *entry_fname, *entry_module, *entry_fun;
118
+	entry_fname = PyUnicode_DecodeFSDefault(PyFCGI_conf.py_entrymod);
119
+	entry_module = PyImport_Import(entry_fname);
120
+	Py_DECREF(entry_fname);
121
+
122
+	if(!entry_module)
123
+	{
124
+		//TODO syslog python error / traceback
125
+		pyfcgi_log(	LOG_CRIT,
126
+			"Unable to import python file '%s'",
127
+			PyFCGI_conf.py_entrymod);
128
+		pyfcgi_log( LOG_INFO,
129
+			"%s", getcwd(NULL, 256));
130
+		log_expt(LOG_ERR);
131
+		sleep(1);
132
+		return NULL;
133
+	}
134
+
135
+	// getting entrypoint function
136
+	entry_fun = PyObject_GetAttrString(entry_module, PyFCGI_conf.py_entryfun);
137
+	Py_DECREF(entry_module);
138
+
139
+	if(!entry_fun)
140
+	{
141
+		//TODO syslog python error / traceback
142
+		pyfcgi_log(	LOG_CRIT,
143
+			"Unable to import object '%s' from '%s'",
144
+			PyFCGI_conf.py_entryfun, PyFCGI_conf.py_entrymod);
145
+		return NULL;
146
+	}
147
+	if(!PyCallable_Check(entry_fun))
148
+	{
149
+		pyfcgi_log( LOG_CRIT,
150
+			"When imported from '%s', '%s' was not a callable",
151
+			PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
152
+		return NULL;
153
+	}
154
+	return entry_fun;
155
+}
156
+
157
+void log_expt(int priority)
158
+{
159
+	if(!PyErr_Occurred())
160
+	{
161
+		pyfcgi_log(priority, "No exception");
162
+		return;
163
+	}
164
+
165
+	PyObject *expt, *expt_str, *expt_bytes, *expt_cls,
166
+		*expt_val, *expt_type, *traceback;
167
+	char *msg, *type, *val;
168
+	int msg_sz;
169
+	char msg_fmt[] = "%s: %s";
170
+
171
+	PyErr_Fetch(&expt_cls, &expt, &traceback);
172
+
173
+	// Fetching exception message using __str__()
174
+	expt_str = PyObject_GetAttrString(expt, "__str__");
175
+	if(!expt_str)
176
+	{
177
+		pyfcgi_log(LOG_ERR, "Unable to fetch __str__ from exception");
178
+	}
179
+	expt_val = PyObject_CallObject(expt_str, NULL);
180
+	expt_bytes = PyUnicode_AsUTF8String(expt_val);
181
+
182
+	msg = PyBytes_AsString(expt_bytes);
183
+	val = strndup(msg, 1024); // TODO check out of mem if !val
184
+
185
+	// Fetching exception class name
186
+	expt_type = PyObject_GetAttrString(expt_cls, "__name__");
187
+	expt_bytes = PyUnicode_AsUTF8String(expt_type);
188
+	msg = PyBytes_AsString(expt_bytes);
189
+	type = strndup(msg, 1024); // TODO check out of mem if !type
190
+
191
+	msg_sz = snprintf(NULL, 0, msg_fmt, type, val);
192
+	msg_sz++;
193
+	msg = malloc(sizeof(char) * msg_sz);
194
+	snprintf(msg, msg_sz, msg_fmt, type, val);
195
+
196
+	pyfcgi_log(priority, msg);
197
+}
198
+

+ 0
- 189
src/pyworker.c View File

@@ -434,48 +434,6 @@ int ctl_get_rep_sz(int ctl_pipe, size_t* rep_sz)
434 434
 	return 0;
435 435
 }
436 436
 
437
-PyObject* import_entrypoint()
438
-{
439
-	PyObject *entry_fname, *entry_module, *entry_fun;
440
-	entry_fname = PyUnicode_DecodeFSDefault(PyFCGI_conf.py_entrymod);
441
-	entry_module = PyImport_Import(entry_fname);
442
-	Py_DECREF(entry_fname);
443
-
444
-	if(!entry_module)
445
-	{
446
-		//TODO syslog python error / traceback
447
-		pyfcgi_log(	LOG_CRIT,
448
-			"Unable to import python file '%s'",
449
-			PyFCGI_conf.py_entrymod);
450
-		pyfcgi_log( LOG_INFO,
451
-			"%s", getcwd(NULL, 256));
452
-		log_expt(LOG_ERR);
453
-		sleep(1);
454
-		return NULL;
455
-	}
456
-
457
-	// getting entrypoint function
458
-	entry_fun = PyObject_GetAttrString(entry_module, PyFCGI_conf.py_entryfun);
459
-	Py_DECREF(entry_module);
460
-
461
-	if(!entry_fun)
462
-	{
463
-		//TODO syslog python error / traceback
464
-		pyfcgi_log(	LOG_CRIT,
465
-			"Unable to import object '%s' from '%s'",
466
-			PyFCGI_conf.py_entryfun, PyFCGI_conf.py_entrymod);
467
-		return NULL;
468
-	}
469
-	if(!PyCallable_Check(entry_fun))
470
-	{
471
-		pyfcgi_log( LOG_CRIT,
472
-			"When imported from '%s', '%s' was not a callable",
473
-			PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
474
-		return NULL;
475
-	}
476
-	return entry_fun;
477
-}
478
-
479 437
 static PyObject* _fetch_pyflush(const char *fdname)
480 438
 {
481 439
 	PyObject *pyfd, *pyflush;
@@ -510,112 +468,6 @@ void fetch_pyflush(PyObject** pystdout_flush, PyObject** pystderr_flush)
510 468
 	*pystderr_flush = _fetch_pyflush("stderr");
511 469
 }
512 470
 
513
-void update_python_path()
514
-{
515
-	wchar_t *ppath, *wcwd, *wtmp;
516
-	char *cwd, *ncwd, *err_fmt;
517
-	size_t wcwd_sz, ppath_sz;
518
-	int err;
519
-	
520
-	// Add a ':' in front of CWD and convert to wchar_t*
521
-	cwd = get_current_dir_name();
522
-	if(!cwd)
523
-	{
524
-		err = errno;
525
-		err_fmt = "Unable to fecth CWD : %s";
526
-		goto update_python_path_err;
527
-	}
528
-	ncwd = malloc(sizeof(char) * (strlen(cwd) + 2));
529
-	if(!ncwd)
530
-	{
531
-		err = errno;
532
-		err_fmt = "Unable to allocate memory for new CWD : %s";
533
-		goto update_python_path_err_cwd;
534
-	}
535
-	ncwd[0] = ':';
536
-	strcpy(ncwd+1, cwd);
537
-	free(cwd);
538
-	cwd = ncwd;
539
-
540
-	wcwd_sz = mbstowcs(NULL, cwd, 0);
541
-	if(wcwd_sz == (size_t)-1)
542
-	{
543
-		err = errno;
544
-		err_fmt = "Unable to convert CWD to wchar : %s";
545
-		goto update_python_path_err_cwd;
546
-	}
547
-	wcwd_sz++;
548
-	wcwd = malloc(sizeof(wchar_t) * wcwd_sz);
549
-	if(!wcwd)
550
-	{
551
-		err = errno;
552
-		err_fmt = "Unable to allocate wchar for CWD : %s";
553
-		goto update_python_path_err_cwd;
554
-
555
-	}
556
-	if(mbstowcs(wcwd, cwd, wcwd_sz) == -1)
557
-	{
558
-		err = errno;
559
-		err_fmt = "Unable to convert CWD to wchar : %s";
560
-		goto update_python_path_err_wcwd;
561
-	}
562
-
563
-	// Fetch, dup & update the python path
564
-	ppath = Py_GetPath();
565
-	if(!ppath)
566
-	{
567
-		if(PyErr_Occurred())
568
-		{
569
-			log_expt(LOG_ALERT);
570
-		}
571
-		else
572
-		{
573
-			pyfcgi_log(LOG_ALERT, "Unable to fetch python path, got NULL.");
574
-		}
575
-		err_fmt = NULL;
576
-		err = 0;
577
-		goto update_python_path_err_wcwd;
578
-	}
579
-	ppath = wcsdup(ppath);
580
-	if(!wcwd)
581
-	{
582
-		err = errno;
583
-		err_fmt = "Unable to dup the python PATH : %s";
584
-		goto update_python_path_err_wcwd;
585
-	}
586
-
587
-	ppath_sz = wcslen(ppath);
588
-	wtmp = realloc(ppath, sizeof(wchar_t) * (ppath_sz + wcwd_sz + 1));
589
-	if(!wtmp)
590
-	{
591
-		err = errno;
592
-		err_fmt = "Unable reallocate new python path : %s";
593
-		goto update_python_path_err_ppath;
594
-	}
595
-	ppath = wtmp;
596
-	wcsncpy(ppath+ppath_sz, wcwd, wcwd_sz);
597
-	Py_SetPath(ppath);
598
-
599
-	free(ppath);
600
-	free(wcwd);
601
-	free(cwd);
602
-
603
-	return;
604
-	
605
-update_python_path_err_ppath:
606
-	free(ppath);
607
-update_python_path_err_wcwd:
608
-	free(wcwd);
609
-update_python_path_err_cwd:
610
-	free(cwd);
611
-update_python_path_err:
612
-	if(err_fmt)
613
-	{
614
-		pyfcgi_log( LOG_ALERT, err_fmt, strerror(err));
615
-	}
616
-	exit(err);
617
-}
618
-
619 471
 void update_python_fd(int pipe_out[2], int pipe_err[2])
620 472
 {
621 473
 	int pri, err;
@@ -816,44 +668,3 @@ void update_pyenv(PyObject *py_osmod, char **environ)
816 668
 
817 669
 }
818 670
 
819
-void log_expt(int priority)
820
-{
821
-	if(!PyErr_Occurred())
822
-	{
823
-		pyfcgi_log(priority, "No exception");
824
-		return;
825
-	}
826
-
827
-	PyObject *expt, *expt_str, *expt_bytes, *expt_cls,
828
-		*expt_val, *expt_type, *traceback;
829
-	char *msg, *type, *val;
830
-	int msg_sz;
831
-	char msg_fmt[] = "%s: %s";
832
-
833
-	PyErr_Fetch(&expt_cls, &expt, &traceback);
834
-
835
-	// Fetching exception message using __str__()
836
-	expt_str = PyObject_GetAttrString(expt, "__str__");
837
-	if(!expt_str)
838
-	{
839
-		pyfcgi_log(LOG_ERR, "Unable to fetch __str__ from exception");
840
-	}
841
-	expt_val = PyObject_CallObject(expt_str, NULL);
842
-	expt_bytes = PyUnicode_AsUTF8String(expt_val);
843
-
844
-	msg = PyBytes_AsString(expt_bytes);
845
-	val = strndup(msg, 1024); // TODO check out of mem if !val
846
-
847
-	// Fetching exception class name
848
-	expt_type = PyObject_GetAttrString(expt_cls, "__name__");
849
-	expt_bytes = PyUnicode_AsUTF8String(expt_type);
850
-	msg = PyBytes_AsString(expt_bytes);
851
-	type = strndup(msg, 1024); // TODO check out of mem if !type
852
-
853
-	msg_sz = snprintf(NULL, 0, msg_fmt, type, val);
854
-	msg_sz++;
855
-	msg = malloc(sizeof(char) * msg_sz);
856
-	snprintf(msg, msg_sz, msg_fmt, type, val);
857
-
858
-	pyfcgi_log(priority, msg);
859
-}

Loading…
Cancel
Save