Ticket #479 (defect)
Opened 3 years ago
Last modified 2 years ago
CherryPy crashes on using seteuid / setegid
Status: closed (fixed)
| Reported by: | pythonic@gmail.com | Assigned to: | rdelon |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | CherryPy code | Keywords: | seteuid setegid socket Resource temporarily unavailable |
| Cc: |
When one use functions os.seteuid, os.setegid cherrypy methods the daemon (started as root user) dies.
Here is the code and the error.
def index(self):
os.setregid(0, 500)
os.setreuid(0, 500)
index.exposed = True
Exception in thread Thread-1:
Traceback (most recent call last):
<snip>
sock, addr = self._sock.accept()
error: (11, 'Resource temporarily unavailable')
==============
Stragely applying following patch fixes it.
--- /usr/lib/python2.4/site-packages/cherrypy/_cpwsgiserver.py
+++ /usr/lib/python2.4/site-packages/cherrypy/_cpwsgiserver.py.new
@@ -339,6 +339,8 @@
# notice keyboard interrupts on Win32, which don't
interrupt
# accept() by default
return
+ except socket.error:
+ return
def stop(self):
"""Gracefully shutdown a server that is serving forever."""
Change History
03/08/06 09:24:02: Modified by pythonic@gmail.com
08/26/06 13:27:45: Modified by fumanchu
From http://groups.google.com/group/cherrypy-users/browse_frm/thread/fa699bc68125a10b:
the "start" method in class CherryPyWSGIServer(object)
"site-packages/cherrypy/_cpwsgiserver.py " file
sets the timeout using the socket.socket.settimeout() method .
---------------------------------------------------------------------------------------------
class CherryPyWSGIServer(object):
.....
def start(self):
'''
run the server forever
'''
# We don't have to trap KeyboardInterrupt or SystemExit here,
# because cherrpy.server already does so, calling self.stop()
for us.
# If you're using this server with another framework, you
should
# trap those exceptions in whatever code block calls start().
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
self.socket.bind(self.bind_addr)
# Timeout so KeyboardInterrupt can be caught on Win32
self.socket.settimeout(1)
------------------------------------------------------------------------------------------------
Now Stepping thru the settimeout implementation in
"Python-2.4.3/Modules/socketmodule.c"
static PyObject *
1470 sock_settimeout(PySocketSockObject *s, PyObject *arg)
1471 {
1472 double timeout;
1473
1474 if (arg == Py_None)
1475 timeout = -1.0;
1476 else {
1477 timeout = PyFloat_AsDouble(arg);
1478 if (timeout < 0.0) {
1479 if (!PyErr_Occurred())
1480
PyErr_SetString(PyExc_ValueError,
1481 "Timeout value
out of range");
1482 return NULL;
1483 }
1484 }
1485
1486 s->sock_timeout = timeout;
1487 internal_setblocking(s, timeout < 0.0);
1488
1489 Py_INCREF(Py_None);
1490 return Py_None;
1491 }
1492
lineno "1487" fisrt sets the "socket fd" to nonblocking(
setblocking(false) ).
and now proceeding to the "socket.accept()" implementation in
"Python-2.4.3/Modules/socketmodule.c"
1362 sock_accept(PySocketSockObject *s)
1363 {
1364 sock_addr_t addrbuf;
1365 SOCKET_T newfd;
1366 socklen_t addrlen;
1367 PyObject *sock = NULL;
1368 PyObject *addr = NULL;
1369 PyObject *res = NULL;
1370 int timeout;
1371
1372 if (!getsockaddrlen(s, &addrlen))
1373 return NULL;
1374 memset(&addrbuf, 0, addrlen);
1375
1376 #ifdef MS_WINDOWS
1377 newfd = INVALID_SOCKET;
1378 #else
1379 newfd = -1;
1380 #endif
1381
1382 if (!IS_SELECTABLE(s))
1383 return select_error();
1384
1385 Py_BEGIN_ALLOW_THREADS
1386 timeout = internal_select(s, 0);
1387 if (!timeout)
1388 newfd = accept(s->sock_fd, (struct sockaddr *)
&addrbuf,
1389 &addrlen);
1390 Py_END_ALLOW_THREADS
1391
1392 if (timeout) {
1393 PyErr_SetString(socket_timeout, "timed out");
1394 return NULL;
1395 }
1396
1397 #ifdef MS_WINDOWS
1398 if (newfd == INVALID_SOCKET)
1399 #else
1400 if (newfd < 0)
1401 #endif
1402 return s->errorhandler();
1403
line no "1386" calls internal_select(s, 0) ; internal_select from
"Python-2.4.3/Modules/socketmodule.c"
649 internal_select(PySocketSockObject *s, int writing)
650 {
651 fd_set fds;
652 struct timeval tv;
653 int n;
654
655 /* Nothing to do unless we're in timeout mode (not
non-blocking) */
656 if (s->sock_timeout <= 0.0)
657 return 0;
658
659 /* Guard against closed socket */
660 if (s->sock_fd < 0)
661 return 0;
662
663 /* Construct the arguments to select */
664 tv.tv_sec = (int)s->sock_timeout;
665 tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) *
1e6);
666 FD_ZERO(&fds);
667 FD_SET(s->sock_fd, &fds);
668
669 /* See if the socket is ready */
670 if (writing)
671 n = select(s->sock_fd+1, NULL, &fds, NULL,
&tv);
672 else
673 n = select(s->sock_fd+1, &fds, NULL, NULL,
&tv);
674 if (n == 0)
675 return 1;
676 return 0;
677 }
On "2.6.11-1.1369_FC4" kernel "select()" seems to be interrupted (
EINTR : Interrupted system call)
when two threads are running when one is doing the select() and other
doing the "seteuid()"
below is the threaded "C" code in . so our above fuctions returns
with "0" and in sock_accept()
line:1388 newfd = accept(s->sock_fd, (struct sockaddr *)
&addrbuf,&addrlen);
must be returning "EAGAIN -> Resource temporarily unavailable" since
we have gone into non-blocking and sockfd is
not readable.
//Start of code
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/fsuid.h>
#include <sys/time.h>
#include <errno.h>
extern int errno ;
void * accept_t (void*arg) ;
void * change_id_t (void*arg);
int bind_new_sock(uint16_t port) ;
void do_accept ( int sock ) ;
#define ACCEPT_T 0
#define CHANGE_ID_T 1
/* main thread */
int main(int argc , char *argv)
{
pthread_t threads [2] ;
int data1[2];
int data2[2];
int code ;
int *status;
/*create the thread */
uint16_t port = 9000 ;
data1[0] = ACCEPT_T ;
data1[1] = bind_new_sock(port) ;
if ( code=pthread_create(&threads[ACCEPT_T],NULL,accept_t,&data1) ) {
fprintf(stderr," %s\n",strerror(code) ) ;
exit(1);
}
data2[0] = CHANGE_ID_T ;
data2[0] = CHANGE_ID_T ;
data2[1] = 501 ;
if (
code=pthread_create(&threads[CHANGE_ID_T],NULL,change_id_t,&data2) ) {
fprintf(stderr,"%s\n", strerror(code) ) ;
exit(1);
}
for(;;)
sleep(2);
}
/* accept thread */
void * accept_t (void*arg)
{
int *data = (int *) arg ;
int tid = data[0] ;
int sock = data[1] ;
for(;;) {
printf ("accept_t : accepting connections \n" ) ;
do_accept( sock ) ;
}
return arg ;
}
void * change_id_t (void*arg)
{
int *data = (int *)arg ;
int tid = data[0] ;
uid_t seuid = data[1] ;
for(;;) {
printf ("change_id_t : changing id\n" ) ;
if ( seteuid(seuid) != 0 )
perror("seteuid()") ;
sleep(2);
if ( seteuid(0) != 0 )
perror("seteuid()") ;
}
return arg ;
}
int bind_new_sock(uint16_t port)
{
int sock;
struct sockaddr_in name;
int oflags;
struct timeval timeout ;
sock = socket (PF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket");
exit (EXIT_FAILURE);
}
oflags = fcntl (sock, F_GETFL, 0);
if ( fcntl (sock, F_SETFL, oflags |= O_NONBLOCK) == -1 )
perror("fcntl()") ;
name.sin_family = AF_INET;
name.sin_port = htons (port);
name.sin_addr.s_addr = htonl (INADDR_ANY);
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
{
perror ("bind");
exit (EXIT_FAILURE);
}
if (listen (sock, 1) < 0)
{
perror ("listen");
exit (EXIT_FAILURE);
}
return sock ;
}
void
do_accept (int sock)
{
struct sockaddr_in newclient;
size_t size;
int new;
fd_set rfds;
struct timeval tv;
int retval;
size = sizeof (newclient);
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
/* timeout */
tv.tv_sec = 15;
tv.tv_usec = 0;
SELECT:
retval = select(sock+1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
perror("select()");
//if ( errno == EINTR ){
//goto SELECT ;
//}
new = accept (sock, (struct sockaddr *) &newclient, &size);
if (new < 0)
{
perror ("accept");
}
}
}
//End Of code
08/26/06 13:48:05: Modified by fumanchu
- status changed from new to closed.
- resolution set to fixed.
OK. We don't want to pass on all socket.errors, though. Isolated fix in [1283] (for CP 2.2 and CP 3).
04/12/07 19:35:02: Modified by crowell
I had a similar problem with code that called seteuid (the pyxmms library). I managed to fix the problem by adding the following into wsgiserver.init:
socket_errors_to_ignore.append(11)
I was using revision 1648.


Looks like it gets EAGAIN on finding difference between effective and real uid.