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

ug_ipc.c

/*
 *
 *   Copyright (C) 2005-2009 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <ug_ipc.h>
#include <ug_utils.h>

static gboolean server_accept  (GIOChannel *source, GIOCondition condition, UgIpc* ipc);
static gboolean server_connect (GIOChannel *source, GIOCondition condition, UgIpc* ipc);

#ifdef _WIN32
// Windows version --------------------------------------------------
//#include <ws2tcpip.h> // ADDRINFO

gboolean    ug_ipc_init_server (UgIpc* ipc)
{
      SOCKET            fd;
      struct sockaddr_in      saddr;
/*
      ADDRINFO    addrInfo;
      ADDRINFO*   ai_result = NULL;

      // AI_PASSIVE     // Socket address will be used in bind() call
      // AI_CANONNAME   // Return canonical name in first ai_canonname
      // AI_NUMERICHOST // Nodename must be a numeric address string
      // AI_NUMERICSERV // Servicename must be a numeric port number
      memset(&addrInfo, 0, sizeof(addrInfo));
      addrInfo.ai_family            = AF_INET;
      addrInfo.ai_socktype    = SOCK_STREAM;
      addrInfo.ai_flags       = AI_NUMERICSERV | AI_PASSIVE;

      if (getaddrinfo ("localhost", UGET_SOCKET_PORT_S, &addrInfo, &ai_result))
            return FALSE;

      fd = socket (AF_INET, SOCK_STREAM, 0);
      if (fd == INVALID_SOCKET)
            return FALSE;

      if (bind(fd, ai_result->ai_addr, ai_result->ai_addrlen) == SOCKET_ERROR) {
            closesocket (fd);
            return FALSE;
      }
*/

      saddr.sin_family = AF_INET;
      saddr.sin_port = htons (UGET_SOCKET_PORT);
      saddr.sin_addr.S_un.S_addr = inet_addr ("127.0.0.1");

      fd = socket (AF_INET, SOCK_STREAM, 0);
      if (fd == INVALID_SOCKET)
            return FALSE;

      if (bind (fd, (struct sockaddr *)&saddr, sizeof (saddr)) == SOCKET_ERROR) {
            closesocket (fd);
            return FALSE;
      }
      if (listen (fd, 5) == SOCKET_ERROR) {
            closesocket (fd);
            return FALSE;
      }

      ipc->argument_func = NULL;
      ipc->filename = NULL;
      ipc->channel  = g_io_channel_win32_new_socket (fd);
      g_io_channel_set_flags (ipc->channel, G_IO_FLAG_NONBLOCK, NULL);
      g_io_channel_set_encoding (ipc->channel, NULL, NULL); // binary
      g_io_add_watch (ipc->channel, G_IO_IN, (GIOFunc)server_accept, ipc);
      return TRUE;
}

gboolean    ug_ipc_init_client (UgIpc* ipc)
{
      SOCKET      fd;
      struct sockaddr_in      saddr;
//    int  error;

      fd = socket (AF_INET, SOCK_STREAM, 0);
//    error = WSAGetLastError();
      if (fd == INVALID_SOCKET)
            return FALSE;

      saddr.sin_family = AF_INET;
      saddr.sin_port = htons (UGET_SOCKET_PORT);
      saddr.sin_addr.S_un.S_addr = inet_addr ("127.0.0.1");

      if (connect (fd, (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) {
            closesocket (fd);
            return FALSE;
      }

      ipc->argument_func = NULL;
      ipc->filename = NULL;
      ipc->channel  = g_io_channel_win32_new_socket (fd);
//    g_io_channel_set_flags (ipc->channel, G_IO_FLAG_NONBLOCK, NULL);
      g_io_channel_set_encoding (ipc->channel, NULL, NULL); // binary
      return TRUE;
}

#else // UNIX
#include <sys/socket.h>    // socket api
#include <unistd.h>        // uid_t and others
#include <errno.h>

#define INVALID_SOCKET  (-1)
#define closesocket           close


gboolean    ug_ipc_init_server (UgIpc* ipc)
{
      int         fd;
      struct sockaddr_un      saddr;

      // test exist server
      if (ug_ipc_init_client (ipc) == TRUE) {
            ug_ipc_finalize (ipc);
            return FALSE;
      }

      saddr.sun_family = AF_UNIX;
      sprintf (saddr.sun_path, "%s/%s-%s",
               g_get_tmp_dir (), UGET_SOCKET_NAME, g_get_user_name ());
      ug_unlink (saddr.sun_path);

      fd = socket (AF_UNIX, SOCK_STREAM, 0);
      if (fd == -1)
            return FALSE;

      if (bind (fd, (struct sockaddr *)&saddr, sizeof (saddr)) == -1) {
            close (fd);
            return FALSE;
      }
      if (listen (fd, 5) == -1) {
            close (fd);
            return FALSE;
      }

      ipc->argument_func = NULL;
      ipc->filename = g_strdup (saddr.sun_path);
      ipc->channel  = g_io_channel_unix_new (fd);
//    g_io_channel_set_flags (ipc->channel, G_IO_FLAG_NONBLOCK, NULL);
      g_io_channel_set_encoding (ipc->channel, NULL, NULL); // binary
      g_io_add_watch (ipc->channel, G_IO_IN, (GIOFunc)server_accept, ipc);

      return TRUE;
}

gboolean    ug_ipc_init_client (UgIpc* ipc)
{
      int         fd;
      uid_t stored_uid, euid;
      struct sockaddr_un      saddr;

      fd = socket (AF_UNIX, SOCK_STREAM, 0);
      if (fd == -1)
            return FALSE;

      saddr.sun_family = AF_UNIX;
      stored_uid = getuid();
      euid = geteuid();
      setuid(euid);
      sprintf (saddr.sun_path, "%s/%s-%s",
               g_get_tmp_dir (), UGET_SOCKET_NAME, g_get_user_name ());
      setreuid(stored_uid, euid);

      if (connect (fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
            close (fd);
            return FALSE;
      }

      ipc->argument_func = NULL;
      ipc->filename = NULL;
      ipc->channel  = g_io_channel_unix_new (fd);
//    g_io_channel_set_flags (ipc->channel, G_IO_FLAG_NONBLOCK, NULL);
      g_io_channel_set_encoding (ipc->channel, NULL, NULL); // binary
      return TRUE;
}
//    End of UNIX code
#endif  // End of _WIN32

void  ug_ipc_finalize (UgIpc* ipc)
{
      SOCKET            ipc_fd;

      if (ipc->channel) {
            ipc_fd = g_io_channel_unix_get_fd (ipc->channel);
            g_io_channel_shutdown (ipc->channel, FALSE, NULL);
            g_io_channel_unref (ipc->channel);
            closesocket (ipc_fd);
            ipc->channel = NULL;
      }
      if (ipc->filename) {
            ug_unlink (ipc->filename);
            g_free (ipc->filename);
            ipc->filename = NULL;
      }
}

static gboolean server_accept (GIOChannel *source, GIOCondition condition, UgIpc* ipc)
{
      SOCKET            source_fd;
      SOCKET            new_fd;
      GIOChannel* new_channel;

      source_fd = g_io_channel_unix_get_fd (source);
      new_fd = accept (source_fd, NULL, NULL);
      if (new_fd == INVALID_SOCKET)
            return TRUE;

#ifdef _WIN32
      new_channel = g_io_channel_win32_new_socket (new_fd);
#else
      new_channel = g_io_channel_unix_new (new_fd);
#endif
      g_io_channel_set_encoding (new_channel, NULL, NULL);  // binary
      g_io_add_watch (new_channel, G_IO_IN | G_IO_HUP, (GIOFunc)server_connect, ipc);

      return TRUE;
}

static gboolean server_connect (GIOChannel *source, GIOCondition condition, UgIpc* ipc)
{
      SOCKET            source_fd;
      GIOStatus   status;
      GString*    gstr;
      GPtrArray*  array;
      GPtrArray*  array_backup;
      gint        array_len;
      gsize       bytes_read;
      gchar       header[3];

      if (condition == G_IO_HUP)
            goto shutdown;

      g_io_channel_read_chars (source, header, 3, &bytes_read, NULL);
      if (bytes_read < 3)
            goto shutdown;
      if (header[0] != 'U' || header[1] != 'G') {
#ifndef NDEBUG
            g_print ("Uget IPC server : Not UG\nData is %c%c\n", header[0], header[1]);
#endif
            goto shutdown;
      }

#ifndef NDEBUG
      g_print ("Uget IPC server : get message from client.\n");
#endif

      array = NULL;
      switch (header[2]) {
      case UG_IPC_SEND:
            gstr = g_string_sized_new (256);
            g_io_channel_read_line_string (source, gstr, &bytes_read, NULL);
            if (bytes_read == 0) {
                  g_string_free (gstr, TRUE);
                  goto error;
            }
            array_len = atoi (gstr->str);
            array = g_ptr_array_sized_new (array_len);
            while (array_len) {
                  status = g_io_channel_read_line_string (source, gstr, &bytes_read, NULL);
                  if (status == G_IO_STATUS_ERROR)
                        break;
                  gstr->str [bytes_read] = 0;
                  g_ptr_array_add (array, g_strndup (gstr->str, gstr->len));
                  array_len--;
            }
            // break;   // don't break, I need response UG_IPC_OK

      case UG_IPC_PING:
            header[2] = UG_IPC_OK;
            g_io_channel_write_chars (source, header, 3, NULL, NULL);
            g_io_channel_flush (source, NULL);
            break;

      default:
            goto error;
      }

//    post processing...
      if (array) {
            array_len = array->len;
            array_backup = g_ptr_array_sized_new (array_len);
            array_backup->len = array_len;
            while (array_len--)
                  g_ptr_array_index (array_backup, array_len) = g_ptr_array_index (array, array_len);
            if (ipc->argument_func)
                  ipc->argument_func (ipc->argument_data, array_backup->len, (char**) array_backup->pdata);
            g_ptr_array_foreach (array, (GFunc) g_free, NULL);
            g_ptr_array_free (array, TRUE);
            g_ptr_array_free (array_backup, TRUE);
      }
      return TRUE;

error:
      header[0] = 'U';
      header[1] = 'G';
      header[2] = UG_IPC_ERROR;
      g_io_channel_write_chars (source, header, 3, NULL, NULL);
      g_io_channel_flush (source, NULL);
      return TRUE;

shutdown:
#ifndef NDEBUG
      g_print ("Uget IPC server : close connection\n");
#endif
      source_fd = g_io_channel_unix_get_fd (source);
      g_io_channel_shutdown (source, TRUE, NULL);
      g_io_channel_unref (source);
      closesocket (source_fd);
      return FALSE;
}

gboolean    ug_ipc_ping (UgIpc* ipc)
{
      GIOStatus   status;
      gsize       n_bytes;
      gchar       header[3];

      header[0] = 'U';
      header[1] = 'G';
      header[2] = UG_IPC_PING;

      status = g_io_channel_write_chars (ipc->channel, header, 3, NULL, NULL);
      if (status == G_IO_STATUS_ERROR)
            return FALSE;
      g_io_channel_flush (ipc->channel, NULL);

      // get response
      g_usleep (100 * 1000);
      status = g_io_channel_read_chars (ipc->channel, header, 3, &n_bytes, NULL);
      if (n_bytes == 3 && header[0] == 'U' && header[1] == 'G' && header[2] == UG_IPC_OK)
            return TRUE;
      return FALSE;
}

gboolean    ug_ipc_send (UgIpc* ipc, int argc, char** argv)
{
      GIOStatus   status;
      GString*    gstr;
      gsize       n_bytes;
      gchar       header[3];
      int               index;

      // header, command
      gstr = g_string_sized_new (256);
      g_string_append (gstr, UGET_SOCKET_HEADER_S);
      g_string_append_c (gstr, UG_IPC_SEND);

      // argc, argv
      g_string_append_printf (gstr, "%d\n", argc);
      for (index=0;  index < argc;  index++) {
            g_string_append (gstr, argv[index]);
            g_string_append_c (gstr, '\n');
      }

      status = g_io_channel_write_chars (ipc->channel, gstr->str, gstr->len, NULL, NULL);
      g_string_free (gstr, TRUE);
      if (status == G_IO_STATUS_ERROR)
            return FALSE;
      g_io_channel_flush (ipc->channel, NULL);

      // get response
      g_usleep (100 * 1000);
      status = g_io_channel_read_chars (ipc->channel, header, 3, &n_bytes, NULL);
      if (n_bytes == 3 && header[0] == 'U' && header[1] == 'G' && header[2] == UG_IPC_OK)
            return TRUE;

      return FALSE;
}


Generated by  Doxygen 1.6.0   Back to index