Logo Search packages:      
Sourcecode: sawfish version File versions  Download package

server.c

/* server.c -- client/server backend
   $Id: server.c,v 1.17 2002/09/02 00:45:33 jsh Exp $

   Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>

   This file is part of sawmill.

   sawmill is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   sawmill is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with sawmill; see the file COPYING.   If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */

#include "sawmill.h"
#include "server.h"

#ifdef HAVE_UNIX

#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdarg.h>
#include <errno.h>

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

/* fd of the socket which clients connect to, or zero. */
static int socket_fd = -1;

/* pathname of the socket. */
static repv socket_name;

DEFSTRING(io_error, "server_make_connection:io");
DEFSYM(server_eval, "server-eval");

#define SOCK_IO(op, sock, buf, len)             \
      char *buf__ = (char *)buf;                \
      int todo__ = len;                   \
      while(todo__ > 0) {                       \
          int this__ = op (sock, buf__, todo__);      \
          if(this__ < 0) {                      \
            if (errno != EINTR)                 \
                return -1;                      \
          }                               \
          else if(this__ == 0)                  \
            break;                              \
          else {                          \
            todo__ -= this__;             \
            buf__ += this__;              \
          }                               \
      }                                   \
      return len - todo__;

static int
sock_write (int fd, void *buf, size_t len)
{
    SOCK_IO (write, fd, buf, len);
}

static int
sock_read (int fd, void *buf, size_t len)
{
    SOCK_IO (read, fd, buf, len);
}

static void
server_handle_request(int fd)
{
    u_char req;
    if(sock_read(fd, &req, 1) != 1)
      goto disconnect;

    /* Need this in case the client code tries to execute a grab */
    save_timestamp (get_server_timestamp ());

    switch(req)
    {
      u_long len;
      repv val;

    case req_eval:
    case req_eval_async:
      /* 1. read length field
         2. read LENGTH bytes of FORM
         3. eval and print FORM
         4. write length of result-string
         5. write LENGTH bytes of result string */
      if(sock_read(fd, &len, sizeof(u_long)) != sizeof(u_long)
         || (val = rep_make_string(len + 1)) == rep_NULL
         || sock_read(fd, rep_STR(val), len) != len)
          goto io_error;
      rep_STR(val)[len] = 0;
      val = rep_call_lisp1 (global_symbol_value (Qserver_eval), val);
      if (req != req_eval_async)
      {
          if(val && rep_STRINGP(val))
          {
            len = rep_STRING_LEN(val);
            if(sock_write(fd, &len, sizeof(u_long)) != sizeof(u_long)
               || sock_write(fd, rep_STR(val), len) != len)
                goto io_error;
          }
          else
          {
            len = 0;
            if(sock_write(fd, &len, sizeof(u_long)) != sizeof(u_long))
                goto io_error;
          }
      }
      break;

    io_error:
      Fsignal(Qerror, rep_LIST_1(rep_VAL(&io_error)));
      return;

    case req_end_of_session:
    disconnect:
      rep_deregister_input_fd(fd);
      close(fd);
    }
    XFlush (dpy);
}

static void
server_accept_connection(int unused_fd)
{
    int confd;
    struct sockaddr_un addr;
    int addr_len = sizeof (addr);

    /* Linux manpage states that we can pass NULL for addr parameters,
       but that has been reported to crash on some systems.. */

    confd = accept(socket_fd, (struct sockaddr *) &addr, &addr_len);

    if(confd >= 0)
    {
      /* Once upon a time, I started reading commands here. I think
         it's cleaner to just register CONFD as an input source */
      rep_register_input_fd(confd, server_handle_request);

      /* CONFD will inherit the properties of SOCKET-FD, i.e. non-
         blocking. Make it block.. */
      rep_unix_set_fd_blocking(confd);
    }
}


/* initialisation */

void
server_init (void)
{
    char namebuf[256];
    repv user, name;

    rep_INTERN(server_eval);
    rep_mark_static (&socket_name);

    if (batch_mode_p ())
      return;

    name = Fsymbol_value (Qcanonical_display_name, Qt);
    user = Fuser_login_name ();

    if(!name || !user || !rep_STRINGP(name) || !rep_STRING(user))
      return;

#ifdef HAVE_SNPRINTF
    snprintf (namebuf, sizeof(namebuf), SAWMILL_SOCK_DIR, rep_STR(user));
#else
    sprintf (namebuf, SAWMILL_SOCK_DIR, rep_STR(user));
#endif

    /* Make the socket directory trying to ensure that it hasn't
       been compromised. */
    if (mkdir (namebuf, 0700) != 0)
    {
      if (errno == EEXIST)
      {
          struct stat st;
          if (stat (namebuf, &st) == 0)
          {
            if (st.st_uid != getuid ())
            {
                fprintf (stderr, "Owner of %s is not the current user\n",
                       namebuf);
                return;
            }
            if (st.st_mode & (S_IRWXG | S_IRWXO))
            {
                fprintf (stderr, "Permissions for %s are too lax\n",
                       namebuf);
                return;
            }
          }
          else
          {
            perror (namebuf);
            return;
          }
      }
      else
      {
          perror (namebuf);
          return;
      }
    }

    /* Add the socket name */
    strcat (namebuf, "/");
    strcat (namebuf, rep_STR(name));

    /* Delete the socket if it exists */
    if(access(namebuf, F_OK) == 0)
    {
      /* Socket already exists. Delete it */
      unlink(namebuf);

      if (access (namebuf, F_OK) == 0)
      {
          fprintf (stderr, "Can't delete %s\n", namebuf);
          return;
      }
    }

    socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(socket_fd >= 0)
    {
      struct sockaddr_un addr;
      addr.sun_family = AF_UNIX;
      strcpy(addr.sun_path, namebuf);
      if(bind(socket_fd, (struct sockaddr *)&addr,
            sizeof(addr.sun_family) + strlen(addr.sun_path) + 1) == 0)
      {
          chmod (namebuf, 0700);
          if(listen(socket_fd, 5) == 0)
          {
            rep_unix_set_fd_nonblocking(socket_fd);
            rep_register_input_fd(socket_fd, server_accept_connection);

            socket_name = rep_string_dup (namebuf);
            return;
          }
          else
            perror ("listen");
      }
      else
          perror ("bind");
      close(socket_fd);
    }
    else
      perror ("socket");
    socket_fd = -1;
}

void
server_kill (void)
{
    if(socket_fd > 0)
    {
      rep_deregister_input_fd(socket_fd);
      close(socket_fd);
      socket_fd = -1;
      unlink(rep_STR(socket_name));
      socket_name = rep_NULL;
    }
}

#endif /* HAVE_UNIX */

Generated by  Doxygen 1.6.0   Back to index