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

stub_server.c

/*
 * $Id: stub_server.c 42 2007-09-07 12:07:51Z hirofuchi $
 *
 * Copyright (C) 2005-2007 Takahiro Hirofuchi
 */

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif

#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef HAVE_LIBWRAP
#include <tcpd.h>
#endif

#define _GNU_SOURCE
#include <getopt.h>
#include <signal.h>

#include "usbip.h"
#include "usbip_network.h"

#include <glib.h>

static const char version[] = PACKAGE_STRING
      " ($Id: stub_server.c 42 2007-09-07 12:07:51Z hirofuchi $)";


static int send_reply_devlist(int sockfd)
{
      int ret;
      struct usbip_exported_device *edev;
      struct op_devlist_reply reply;


      reply.ndev = 0;

      /* how many devices are exported ? */
      dlist_for_each_data(stub_driver->edev_list, edev, struct usbip_exported_device) {
            reply.ndev += 1;
      }

      dbg("%d devices are exported", reply.ndev);

      ret = usbip_send_op_common(sockfd, OP_REP_DEVLIST,  ST_OK);
      if (ret < 0) {
            err("send op_common");
            return ret;
      }

      PACK_OP_DEVLIST_REPLY(1, &reply);

      ret = usbip_send(sockfd, (void *) &reply, sizeof(reply));
      if (ret < 0) {
            err("send op_devlist_reply");
            return ret;
      }

      dlist_for_each_data(stub_driver->edev_list, edev, struct usbip_exported_device) {
            struct usb_device pdu_udev;

            dump_usb_device(&edev->udev);
            memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
            pack_usb_device(1, &pdu_udev);

            ret = usbip_send(sockfd, (void *) &pdu_udev, sizeof(pdu_udev));
            if (ret < 0) {
                  err("send pdu_udev");
                  return ret;
            }

            for (int i=0; i < edev->udev.bNumInterfaces; i++) {
                  struct usb_interface pdu_uinf;

                  dump_usb_interface(&edev->uinf[i]);
                  memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
                  pack_usb_interface(1, &pdu_uinf);

                  ret = usbip_send(sockfd, (void *) &pdu_uinf, sizeof(pdu_uinf));
                  if (ret < 0) {
                        err("send pdu_uinf");
                        return ret;
                  }
            }
      }

      return 0;
}


static int recv_request_devlist(int sockfd)
{
      int ret;
      struct op_devlist_request req;

      bzero(&req, sizeof(req));

      ret = usbip_recv(sockfd, (void *) &req, sizeof(req));
      if (ret < 0) {
            err("recv devlist request");
            return -1;
      }

      ret = send_reply_devlist(sockfd);
      if (ret < 0) {
            err("send devlist reply");
            return -1;
      }

      return 0;
}


static int recv_request_import(int sockfd)
{
      int ret;
      struct op_import_request req;
      struct op_common reply;
      struct usbip_exported_device *edev;
      int found = 0;
      int error = 0;

      bzero(&req, sizeof(req));
      bzero(&reply, sizeof(reply));

      ret = usbip_recv(sockfd, (void *) &req, sizeof(req));
      if (ret < 0) {
            err("recv import request");
            return -1;
      }

      PACK_OP_IMPORT_REQUEST(0, &req);

      dlist_for_each_data(stub_driver->edev_list, edev, struct usbip_exported_device) {
            if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
                  dbg("found requested device %s", req.busid);
                  found = 1;
                  break;
            }
      }

      if (found) {
            /* should set TCP_NODELAY for usbip */
            usbip_set_nodelay(sockfd);

            /* export_device needs a TCP/IP socket descriptor */
            ret = usbip_stub_export_device(edev, sockfd);
            if (ret < 0)
                  error = 1;
      } else {
            info("not found requested device %s", req.busid);
            error = 1;
      }


      ret = usbip_send_op_common(sockfd, OP_REP_IMPORT, (!error ? ST_OK : ST_NA));
      if (ret < 0) {
            err("send import reply");
            return -1;
      }

      if (!error) {
            struct usb_device pdu_udev;

            memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
            pack_usb_device(1, &pdu_udev);

            ret = usbip_send(sockfd, (void *) &pdu_udev, sizeof(pdu_udev));
            if (ret < 0) {
                  err("send devinfo");
                  return -1;
            }
      }

      return 0;
}



static int recv_pdu(int sockfd)
{
      int ret;
      uint16_t code = OP_UNSPEC;


      ret = usbip_recv_op_common(sockfd, &code);
      if (ret < 0) {
            err("recv op_common, %d", ret);
            return ret;
      }


      ret = usbip_stub_refresh_device_list();
      if (ret < 0)
            return -1;

      switch(code) {
            case OP_REQ_DEVLIST:
                  ret = recv_request_devlist(sockfd);
                  break;

            case OP_REQ_IMPORT:
                  ret = recv_request_import(sockfd);
                  break;

            case OP_REQ_DEVINFO:
            case OP_REQ_CRYPKEY:

            default:
                  err("unknown op_code, %d", code);
                  ret = -1;
      }


      return ret;
}




static void log_addrinfo(struct addrinfo *ai)
{
      int ret;
      char hbuf[NI_MAXHOST];
      char sbuf[NI_MAXSERV];

      ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
                  sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
      if (ret)
            err("getnameinfo, %s", gai_strerror(ret));

      info("listen at [%s]:%s", hbuf, sbuf);
}

static struct addrinfo *my_getaddrinfo(char *host, int ai_family)
{
      int ret;
      struct addrinfo hints, *ai_head;

      bzero(&hints, sizeof(hints));

      hints.ai_family   = ai_family;
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags    = AI_PASSIVE;

      ret = getaddrinfo(host, USBIP_PORT_STRING, &hints, &ai_head);
      if (ret) {
            err("%s: %s", USBIP_PORT_STRING, gai_strerror(ret));
            return NULL;
      }

      return ai_head;
}

#define MAXSOCK 20
static int listen_all_addrinfo(struct addrinfo *ai_head, int lsock[])
{
      struct addrinfo *ai;
      int n = 0;        /* number of sockets */

      for (ai = ai_head; ai && n < MAXSOCK; ai = ai->ai_next) {
            int ret;

            lsock[n] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
            if (lsock[n] < 0)
                  continue;

            usbip_set_reuseaddr(lsock[n]);
            usbip_set_nodelay(lsock[n]);

            if (lsock[n] >= FD_SETSIZE) {
                  close(lsock[n]);
                  lsock[n] = -1;
                  continue;
            }

            ret = bind(lsock[n], ai->ai_addr, ai->ai_addrlen);
            if (ret < 0) {
                  close(lsock[n]);
                  lsock[n] = -1;
                  continue;
            }

            ret = listen(lsock[n], SOMAXCONN);
            if (ret < 0) {
                  close(lsock[n]);
                  lsock[n] = -1;
                  continue;
            }

            log_addrinfo(ai);

            /* next if succeed */
            n++;
      }

      if (n == 0) {
            err("no socket to listen to");
            return -1;
      }

      dbg("listen %d address%s", n, (n==1)?"":"es");

      return n;
}

#ifdef HAVE_LIBWRAP
static int tcpd_auth(int csock)
{
      int ret;
      struct request_info request;

      request_init(&request, RQ_DAEMON, "usbipd", RQ_FILE, csock, 0);

      fromhost(&request);

      ret = hosts_access(&request);
      if (!ret)
            return -1;

      return 0;
}
#endif

static int my_accept(int lsock)
{
      int csock;
      struct sockaddr_storage ss;
      socklen_t len = sizeof(ss);
      char host[NI_MAXHOST], port[NI_MAXSERV];
      int ret;

      bzero(&ss, sizeof(ss));

      csock = accept(lsock, (struct sockaddr *) &ss, &len);
      if (csock < 0) {
            err("accept");
            return -1;
      }

      ret = getnameinfo((struct sockaddr *) &ss, len,
                  host, sizeof(host), port, sizeof(port),
                  (NI_NUMERICHOST | NI_NUMERICSERV));
      if (ret)
            err("getnameinfo, %s", gai_strerror(ret));

#ifdef HAVE_LIBWRAP
      ret = tcpd_auth(csock);
      if (ret < 0) {
            info("deny access from %s", host);
            close(csock);
            return -1;
      }
#endif

      info("connected from %s:%s", host, port);

      return csock;
}


GMainLoop *main_loop;

static void signal_handler(int i)
{
      dbg("signal catched, code %d", i);

      if (main_loop)
            g_main_loop_quit(main_loop);
}

static void set_signal(void)
{
      struct sigaction act;

      bzero(&act, sizeof(act));
      act.sa_handler = signal_handler;
      sigemptyset(&act.sa_mask);
      sigaction(SIGTERM, &act, NULL);
      sigaction(SIGINT, &act, NULL);
}


gboolean process_comming_request(GIOChannel *gio, GIOCondition condition, gpointer data)
{
      int ret;

      if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
            g_error("unknown condition");


      if (condition & G_IO_IN) {
            int lsock;
            int csock;

            lsock = g_io_channel_unix_get_fd(gio);

            csock = my_accept(lsock);
            if (csock < 0)
                  return TRUE;

            ret = recv_pdu(csock);
            if (ret < 0)
                  err("process recieved pdu");

            close(csock);
      }

      return TRUE;
}


static void do_standalone_mode(gboolean daemonize)
{
      int ret;
      int lsock[MAXSOCK];
      struct addrinfo *ai_head;
      int n;



      ret = usbip_names_init(USBIDS_FILE);
      if (ret)
            err("open usb.ids");

      ret = usbip_stub_driver_open();
      if (ret < 0)
            g_error("driver open failed");

      if (daemonize) {
            if (daemon(0,0) < 0)
                  g_error("daemonizing failed: %s", g_strerror(errno));

            usbip_use_syslog = 1;
      }

      set_signal();

      ai_head = my_getaddrinfo(NULL, PF_UNSPEC);
      if (!ai_head)
            return;

      n = listen_all_addrinfo(ai_head, lsock);
      if (n <= 0)
            g_error("no socket to listen to");

      for (int i = 0; i < n; i++) {
            GIOChannel *gio;

            gio = g_io_channel_unix_new(lsock[i]);
            g_io_add_watch(gio, (G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
                        process_comming_request, NULL);
      }


      info("usbipd start (%s)", version);


      main_loop = g_main_loop_new(FALSE, FALSE);
      g_main_loop_run(main_loop);

      info("shutdown");

      freeaddrinfo(ai_head);
      usbip_names_free();
      usbip_stub_driver_close();

      return;
}


static const char help_message[] = "\
Usage: usbipd [options]                   \n\
      -D, --daemon                        \n\
            Run as a daemon process.      \n\
                                    \n\
      -d, --debug                   \n\
            Print debugging information.  \n\
                                    \n\
      -v, --version                       \n\
            Show version.                 \n\
                                    \n\
      -h, --help                    \n\
            Print this help.        \n";

static void show_help(void)
{
      printf("%s", help_message);
}

static const struct option longopts[] = {
      {"daemon",  no_argument,      NULL, 'D'},
      {"debug",   no_argument,      NULL, 'd'},
      {"version", no_argument,      NULL, 'v'},
      {"help",    no_argument,      NULL, 'h'},
      {NULL,            0,          NULL,  0}
};

int main(int argc, char *argv[])
{
      gboolean daemonize = FALSE;

      enum {
            cmd_standalone_mode = 1,
            cmd_help,
            cmd_version
      } cmd = cmd_standalone_mode;


      usbip_use_stderr = 1;
      usbip_use_syslog = 0;

      if (geteuid() != 0)
            g_warning("running non-root?");

      for (;;) {
            int c;
            int index = 0;

            c = getopt_long(argc, argv, "vhdD", longopts, &index);

            if (c == -1)
                  break;

            switch (c) {
                  case 'd':
                        usbip_use_debug = 1;
                        continue;
                  case 'v':
                        cmd = cmd_version;
                        break;
                  case 'h':
                        cmd = cmd_help;
                        break;
                  case 'D':
                        daemonize = TRUE;
                        break;
                  case '?':
                        show_help();
                        exit(EXIT_FAILURE);
                  default:
                        err("getopt");
            }
      }

      switch (cmd) {
            case cmd_standalone_mode:
                  do_standalone_mode(daemonize);
                  break;
            case cmd_version:
                  printf("%s\n", version);
                  break;
            case cmd_help:
                  show_help();
                  break;
            default:
                  info("unknown cmd");
                  show_help();
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index