Browse Source

PyFCGI now handles fcgi socket creation

Add a -l --listen option to set the socket path or address:port
Fix a bug using thread safe functions from libfcgi
Yann Weber 4 years ago
parent
commit
f685f83369
7 changed files with 70 additions and 11 deletions
  1. 3
    3
      README
  2. 9
    4
      docs/man/man1/pyfcgi.1
  3. 16
    1
      include/conf.h
  4. 4
    0
      src/conf.c
  5. 4
    1
      src/main.c
  6. 18
    2
      src/pyworker.c
  7. 16
    0
      src/responder.c

+ 3
- 3
README View File

9
 	$ ./configure
9
 	$ ./configure
10
 	$ make
10
 	$ make
11
 	# To run foo_pep333.entrypoint() PEP333 application
11
 	# To run foo_pep333.entrypoint() PEP333 application
12
-	$ spawn-fcgi -d . -n -p 9000 -a 127.0.0.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint
12
+	$ src/pyfcgi -l '127.0.0.1:9000' -S -e foo_pep333 -E entrypoint
13
 or
13
 or
14
 	# To run foo.entrypoint() sending to FCGI python stdout
14
 	# To run foo.entrypoint() sending to FCGI python stdout
15
-	$ spawn-fcgi -d . -n -p 9000 -a 127.0.0.1 -- src/pyfcgi -S -e foo -E entrypoint -A
15
+	$ src/pyfcgi -l '127.0.0.1:9000' -S -e foo -E entrypoint -A
16
 
16
 
17
 
17
 
18
 configure script determine python flags, libs & includes paths using
18
 configure script determine python flags, libs & includes paths using
23
 ---------
23
 ---------
24
 	$ ./configure PYTHON_CONFIG_PATH=/usr/bin/python3dm-config --enable-debug
24
 	$ ./configure PYTHON_CONFIG_PATH=/usr/bin/python3dm-config --enable-debug
25
 	$ make clean && make
25
 	$ make clean && make
26
-	$ valgrind --log-file=/tmp/val.log --trace-children=yes spawn-fcgi -d . -n -p 9000 -a 127.0.0.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint -L '/tmp/foo.log;0xff;{datetime} {msg} {ident}'
26
+	$ valgrind --log-file=/tmp/val.log --trace-children=yes src/pyfcgi -S -e foo_pep333 -E entrypoint -L '/tmp/foo.log;0xff;{datetime} {msg} {ident}'
27
 
27
 
28
 logging to file example :
28
 logging to file example :
29
 -------------------------
29
 -------------------------

+ 9
- 4
docs/man/man1/pyfcgi.1 View File

1
-.TH "pyfcgi" 1 "Sat Aug 24 2019" "Version 0.0.1" "PyFCGI" \" -*- nroff -*-
1
+.TH "pyfcgi" 1 "Mon Oct 28 2019" "Version 0.0.1" "PyFCGI" \" -*- nroff -*-
2
 .ad l
2
 .ad l
3
 .nh
3
 .nh
4
 .SH NAME
4
 .SH NAME
24
 Display pyfcgi and Python version and exit 
24
 Display pyfcgi and Python version and exit 
25
 .RE
25
 .RE
26
 .PP
26
 .PP
27
-\fB-c --config=FILE\fP
27
+\fB-C --config=FILE\fP
28
 .RS 4
28
 .RS 4
29
 load a configuration file 
29
 load a configuration file 
30
 .RE
30
 .RE
31
 .PP
31
 .PP
32
+\fB-l --listen=SOCK_PATH\fP
33
+.RS 4
34
+fcgi listen socket path\&. For TCP socket use 'IPv4:PORT' syntax ( '127\&.0\&.0\&.1:9000' by default) 
35
+.RE
36
+.PP
32
 \fB-e --pymodule=MODULE_NAME\fP
37
 \fB-e --pymodule=MODULE_NAME\fP
33
 .RS 4
38
 .RS 4
34
 python entrypoint module name 
39
 python entrypoint module name 
134
 .PP
139
 .PP
135
 To run foo_pep333\&.entrypoint() PEP333 application :
140
 To run foo_pep333\&.entrypoint() PEP333 application :
136
 .PP
141
 .PP
137
-spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S \\ -e foo_pep333 -E entrypoint
142
+spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint
138
 .PP
143
 .PP
139
 Logfile example :
144
 Logfile example :
140
 .PP
145
 .PP
141
-spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S \\ -e foo_pep333 -E entrypoint \\ -L '/tmp/foo\&.log;0xff;{datetime} {msg} {ident}'
146
+spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint -L '/tmp/foo\&.log;0xff;{datetime} {msg} {ident}'
142
 .SH "AUTHOR"
147
 .SH "AUTHOR"
143
 .PP
148
 .PP
144
 Written by Yann Weber <yann.weber@member.fsf.org>
149
 Written by Yann Weber <yann.weber@member.fsf.org>

+ 16
- 1
include/conf.h View File

74
 /**@ingroup ret_status */
74
 /**@ingroup ret_status */
75
 #define PYFCGI_FATAL 128
75
 #define PYFCGI_FATAL 128
76
 
76
 
77
+/**@brief Backlog argument for socket creation */
78
+#define PYFCGI_SOCK_BACKLOG 100
79
+
77
 #define PYFCGI_NAME "spawn-fcgi [OPTIONS] -- pyfcgi"
80
 #define PYFCGI_NAME "spawn-fcgi [OPTIONS] -- pyfcgi"
78
 
81
 
79
-#define PYFCGI_SHORT_OPT "Ce:E:Aw:W:m:ft:L:SPs:vVh"
82
+#define PYFCGI_SHORT_OPT "C:l:e:E:Aw:W:m:ft:L:SPs:vVh"
80
 
83
 
81
 #define PYFCGI_LONG_OPT { \
84
 #define PYFCGI_LONG_OPT { \
82
 	{"config", required_argument, 0, 'C'},\
85
 	{"config", required_argument, 0, 'C'},\
86
+	{"listen", required_argument, 0, 'l'},\
83
 	{"pymodule", required_argument, 0, 'e'},\
87
 	{"pymodule", required_argument, 0, 'e'},\
84
 	{"pyapp", required_argument, 0, 'E'},\
88
 	{"pyapp", required_argument, 0, 'E'},\
85
 	{"alt-io", no_argument, 0, 'A'},\
89
 	{"alt-io", no_argument, 0, 'A'},\
100
 
104
 
101
 #define PYFCGI_OPT_HELP {\
105
 #define PYFCGI_OPT_HELP {\
102
 	{"Load options from configuration file", "CONFIG"},\
106
 	{"Load options from configuration file", "CONFIG"},\
107
+	{"Listen socket path (a path for UNIX socket or a 'IPV4:PORT' string)", "SOCK_PATH"},\
103
 	{"Search application function in given python module", "MODULE_NAME"},\
108
 	{"Search application function in given python module", "MODULE_NAME"},\
104
 	{"Python application entrypoint function name", "FUNC_NAME"},\
109
 	{"Python application entrypoint function name", "FUNC_NAME"},\
105
 	{"Use stdout to communicate with web server instead of entrypoint return as specified in PEP 333", NULL},\
110
 	{"Use stdout to communicate with web server instead of entrypoint return as specified in PEP 333", NULL},\
176
 	/**@brief Stores parent process PID */
181
 	/**@brief Stores parent process PID */
177
 	pid_t ppid;
182
 	pid_t ppid;
178
 
183
 
184
+	/**@brief Returned by FCGX_OpenSocket for responder process */
185
+	int fcgi_socket;
186
+
187
+	/**@brief Request information (local to each worker) */
188
+	FCGX_Request fcgi_request;
189
+
179
 	/**@brief Stores the watchdog timer */
190
 	/**@brief Stores the watchdog timer */
180
 	timer_t wd_timer;
191
 	timer_t wd_timer;
181
 	/**@brief Security timer sending a sigkill */
192
 	/**@brief Security timer sending a sigkill */
215
 	/** @brief Stores pidfile path */
226
 	/** @brief Stores pidfile path */
216
 	char *pidfile;
227
 	char *pidfile;
217
 
228
 
229
+	/**@brief Stores the socket path argument
230
+	 * @ingroup conf_glob
231
+	 * Can be a file path for a UNIX socket or IPv4:PORT string */
232
+	char *sock_path;
218
 	/**@brief Entrypoint module name
233
 	/**@brief Entrypoint module name
219
 	 * @ingroup conf_glob */
234
 	 * @ingroup conf_glob */
220
 	char *py_entrymod;
235
 	char *py_entrymod;

+ 4
- 0
src/conf.c View File

40
 void default_conf()
40
 void default_conf()
41
 {
41
 {
42
 	memset(&PyFCGI_conf, 0, sizeof(pyfcgi_conf_t));
42
 	memset(&PyFCGI_conf, 0, sizeof(pyfcgi_conf_t));
43
+	PyFCGI_conf.sock_path = "127.0.0.1:9000";
43
 	PyFCGI_conf.context.pid = getpid();
44
 	PyFCGI_conf.context.pid = getpid();
44
 	PyFCGI_conf.min_wrk = 1;
45
 	PyFCGI_conf.min_wrk = 1;
45
 	PyFCGI_conf.max_wrk = 5;
46
 	PyFCGI_conf.max_wrk = 5;
72
 			case 'C':
73
 			case 'C':
73
 				dprintf(2, "Config parser not yet implemented :'(\n");
74
 				dprintf(2, "Config parser not yet implemented :'(\n");
74
 				exit(1);
75
 				exit(1);
76
+			case 'l':
77
+				PyFCGI_conf.sock_path = strdup(optarg);
78
+				break;
75
 			case 'e':
79
 			case 'e':
76
 				PyFCGI_conf.py_entrymod = strdup(optarg);
80
 				PyFCGI_conf.py_entrymod = strdup(optarg);
77
 				break;
81
 				break;

+ 4
- 1
src/main.c View File

362
  * Display help and exit
362
  * Display help and exit
363
  * @par -V --version
363
  * @par -V --version
364
  * Display pyfcgi and Python version and exit
364
  * Display pyfcgi and Python version and exit
365
- * @par -c --config=FILE
365
+ * @par -C --config=FILE
366
  * load a configuration file
366
  * load a configuration file
367
+ * @par -l --listen=SOCK_PATH
368
+ * fcgi listen socket path. For TCP socket use "IPv4:PORT" syntax (
369
+ * "127.0.0.1:9000" by default)
367
  * @par -e --pymodule=MODULE_NAME
370
  * @par -e --pymodule=MODULE_NAME
368
  * python entrypoint module name
371
  * python entrypoint module name
369
  * @par -E --pyapp=FUNC_NAME
372
  * @par -E --pyapp=FUNC_NAME

+ 18
- 2
src/pyworker.c View File

46
 	int count, pipe_out[2], pipe_err[2];
46
 	int count, pipe_out[2], pipe_err[2];
47
 	int max_reqs;
47
 	int max_reqs;
48
 	struct timeval start, stop;
48
 	struct timeval start, stop;
49
+	FCGX_Request *request;
49
 
50
 
50
 	max_reqs = PyFCGI_conf.max_reqs;
51
 	max_reqs = PyFCGI_conf.max_reqs;
51
 
52
 
72
 
73
 
73
 	start_response = get_start_response();
74
 	start_response = get_start_response();
74
 
75
 
76
+	// Initialise FCGI request
77
+	request = &(PyFCGI_conf.context.fcgi_request);
78
+	if(FCGX_InitRequest(request, PyFCGI_conf.context.fcgi_socket,
79
+		FCGI_FAIL_ACCEPT_ON_INTR) == -1)
80
+	{
81
+		pyfcgi_log(LOG_ALERT, "Unable to init FCGI request");
82
+		exit(PYFCGI_FATAL);
83
+	}
84
+
75
 	_worker_idle = 0;
85
 	_worker_idle = 0;
76
 	worker_set_idle();
86
 	worker_set_idle();
77
 	// requests accepting loop
87
 	// requests accepting loop
78
 	count = 0;
88
 	count = 0;
79
 	while ((!count || count != max_reqs) &&
89
 	while ((!count || count != max_reqs) &&
80
-		FCGX_Accept(&in_stream, &out_stream, &err_stream, &envp) >= 0)
90
+		FCGX_Accept_r(request) == 0)
81
 	{
91
 	{
92
+		
93
+		in_stream = request->in;
94
+		out_stream = request->out;
95
+		err_stream = request->err;
96
+		envp = request->envp;
97
+
82
 		pyfcgi_wd_arm();
98
 		pyfcgi_wd_arm();
83
 		sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
99
 		sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
84
 		gettimeofday(&start, NULL);
100
 		gettimeofday(&start, NULL);
136
 		FCGX_FClose(out_stream);
152
 		FCGX_FClose(out_stream);
137
 		FCGX_FClose(in_stream);
153
 		FCGX_FClose(in_stream);
138
 		FCGX_FClose(err_stream);
154
 		FCGX_FClose(err_stream);
139
-		FCGI_Finish();
155
+		FCGX_Finish_r(request);
140
 
156
 
141
 		gettimeofday(&stop, NULL);
157
 		gettimeofday(&stop, NULL);
142
 		stop.tv_sec = stop.tv_sec - start.tv_sec;
158
 		stop.tv_sec = stop.tv_sec - start.tv_sec;

+ 16
- 0
src/responder.c View File

92
 	}
92
 	}
93
 	bzero(PyFCGI_conf.context.wrk_pids,
93
 	bzero(PyFCGI_conf.context.wrk_pids,
94
 		sizeof(pid_t) * (PyFCGI_conf.max_wrk + 1));
94
 		sizeof(pid_t) * (PyFCGI_conf.max_wrk + 1));
95
+	
96
+	// Open FCGI listen socket
97
+	PyFCGI_conf.context.fcgi_socket = FCGX_OpenSocket(PyFCGI_conf.sock_path,
98
+		PYFCGI_SOCK_BACKLOG);
99
+	if(PyFCGI_conf.context.fcgi_socket == -1)
100
+	{
101
+		pyfcgi_log(LOG_ALERT, "Unable to open socket : '%s'",
102
+			PyFCGI_conf.sock_path);
103
+		clean_exit(PYFCGI_FATAL);
104
+	}
105
+	if(FCGX_Init() != 0)
106
+	{
107
+		pyfcgi_log(LOG_ALERT, "Unable to init libfcgi");
108
+		clean_exit(PYFCGI_FATAL);
109
+	}
110
+	pyfcgi_log(LOG_INFO, "Listening on %s", PyFCGI_conf.sock_path);
95
 }
111
 }
96
 
112
 
97
 int responder_loop()
113
 int responder_loop()

Loading…
Cancel
Save