Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Ticket #479 (defect)

Opened 2 years ago

Last modified 1 year 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

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

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.

Hosted by WebFaction

Log in as guest/cpguest to create tickets