home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / script-fu / script-fu-server.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-28  |  13.6 KB  |  594 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include <stdarg.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <ctype.h>
  24. #include <unistd.h>
  25. #include <time.h>
  26. #include <sys/time.h>
  27. #include <sys/types.h>
  28. #include <sys/socket.h>
  29. #include <netinet/in.h>
  30. #include <arpa/inet.h>
  31. #include <netdb.h>
  32.  
  33. #include "config.h"
  34.  
  35. #ifdef HAVE_SYS_SELECT_H
  36. #include <sys/select.h>
  37. #endif /* HAVE_SYS_SELECT_H */
  38.  
  39. #include "libgimp/gimp.h"
  40. #include "libgimp/gimpui.h"
  41.  
  42. #include "script-fu-intl.h"
  43.  
  44. #include "gtk/gtk.h"
  45. #include "siod.h"
  46. #include "script-fu-server.h"
  47.  
  48. #define COMMAND_HEADER  3
  49. #define RESPONSE_HEADER 4
  50. #define MAGIC   'G'
  51.  
  52. #ifdef NO_DIFFTIME
  53. #define difftime(a,b) (((double)(a)) - ((double)(b)))
  54. #endif
  55.  
  56. #ifndef NO_FD_SET
  57. #  define SELECT_MASK fd_set
  58. #else
  59. #  ifndef _AIX
  60.      typedef long fd_mask;
  61. #  endif
  62. #  if defined(_IBMR2)
  63. #    define SELECT_MASK void
  64. #  else
  65. #    define SELECT_MASK int
  66. #  endif
  67. #endif
  68.  
  69.  
  70. /*  image information  */
  71.  
  72. /*  Header format for incoming commands...
  73.  *    bytes: 1          2          3
  74.  *           MAGIC      CMD_LEN_H  CMD_LEN_L
  75.  */
  76.  
  77. /*  Header format for outgoing responses...
  78.  *    bytes: 1          2          3          4
  79.  *           MAGIC      ERROR?     RSP_LEN_H  RSP_LEN_L
  80.  */
  81.  
  82. #define MAGIC_BYTE      0
  83.  
  84. #define CMD_LEN_H_BYTE  1
  85. #define CMD_LEN_L_BYTE  2
  86.  
  87. #define ERROR           1
  88. #define RSP_LEN_H_BYTE  2
  89. #define RSP_LEN_L_BYTE  3
  90.  
  91. /*
  92.  *  Local Structures
  93.  */
  94.  
  95. typedef struct
  96. {
  97.   gchar *command;
  98.   gint   filedes;
  99.   gint   request_no;
  100. } SFCommand;
  101.  
  102. typedef struct
  103. {
  104.   GtkWidget *port_entry;
  105.   GtkWidget *log_entry;
  106.  
  107.   gint       port;
  108.   gchar     *logfile;
  109.  
  110.   gint       run;
  111. } ServerInterface;
  112.  
  113. /*
  114.  *  Local Functions
  115.  */
  116.  
  117. static void   server_start       (gint       port,
  118.                   gchar     *logfile);
  119. static gint   execute_command    (SFCommand *cmd);
  120. static gint   read_from_client   (gint       filedes);
  121. static gint   make_socket        (guint      port);
  122. static void   server_log         (gchar     *format,
  123.                      ...);
  124. static void   server_quit        (void);
  125.  
  126. static gint   server_interface   (void);
  127. static void   ok_callback        (GtkWidget *widget,
  128.                   gpointer   data);
  129.  
  130. /*
  131.  *  Global variables
  132.  */
  133. gint server_mode = FALSE;
  134.  
  135. /*
  136.  *  Local variables
  137.  */
  138. static gint   server_sock;
  139. static GList *command_queue = NULL;
  140. static gint   queue_length = 0;
  141. static gint   request_no = 0;
  142. static FILE  *server_log_file = NULL;
  143. static GHashTable *clientname_ht = NULL;
  144. static SELECT_MASK server_active, server_read;
  145.  
  146. static ServerInterface sint =
  147. {
  148.   NULL,  /*  port entry widget  */
  149.   NULL,  /*  log entry widget  */
  150.  
  151.   10008, /*  default port number  */
  152.   NULL,  /*  use stdout  */
  153.  
  154.   FALSE  /*  run  */
  155. };
  156.  
  157. extern gint   script_fu_done;
  158. extern char   siod_err_msg[];
  159. extern LISP   repl_return_val;
  160.  
  161. /*
  162.  *  Server interface functions
  163.  */
  164.  
  165. void
  166. script_fu_server_run (char     *name,
  167.               int       nparams,
  168.               GimpParam   *params,
  169.               int      *nreturn_vals,
  170.               GimpParam  **return_vals)
  171. {
  172.   static GimpParam values[1];
  173.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  174.   GimpRunModeType run_mode;
  175.  
  176.   run_mode = params[0].data.d_int32;
  177.  
  178.   switch (run_mode)
  179.     {
  180.     case GIMP_RUN_INTERACTIVE:
  181.       if (server_interface ())
  182.     {
  183.       server_mode = TRUE;
  184.  
  185.       /*  Start the server  */
  186.       server_start (sint.port, sint.logfile);
  187.     }
  188.       break;
  189.  
  190.     case GIMP_RUN_NONINTERACTIVE:
  191.       /*  Set server_mode to TRUE  */
  192.       server_mode = TRUE;
  193.  
  194.       /*  Start the server  */
  195.       server_start (params[1].data.d_int32, params[2].data.d_string);
  196.       break;
  197.  
  198.     case GIMP_RUN_WITH_LAST_VALS:
  199.       status = GIMP_PDB_CALLING_ERROR;
  200.       g_warning ("Script-Fu server does handle \"GIMP_RUN_WITH_LAST_VALS\"");
  201.  
  202.     default:
  203.       break;
  204.     }
  205.  
  206.   *nreturn_vals = 1;
  207.   *return_vals = values;
  208.  
  209.   values[0].type = GIMP_PDB_STATUS;
  210.   values[0].data.d_status = status;
  211. }
  212.  
  213. void
  214. script_fu_server_listen (gint timeout)
  215. {
  216.   struct sockaddr_in clientname;
  217.   struct timeval tv;
  218.   struct timeval *tvp;
  219.   gint i;
  220.   gint size;
  221.  
  222.   /*  Set time struct  */
  223.   if (timeout)
  224.     {
  225.       tv.tv_sec = timeout / 1000;
  226.       tv.tv_usec = timeout % 1000;
  227.       tvp = &tv;
  228.     }
  229.   else
  230.     tvp = NULL;
  231.  
  232.   /* Block until input arrives on one or more active sockets or timeout occurs. */
  233.   server_read = server_active;
  234.   if (select (FD_SETSIZE, &server_read, NULL, NULL, tvp) < 0)
  235.     {
  236.       perror ("select");
  237.       return;
  238.     }
  239.  
  240.   /* Service all the sockets with input pending. */
  241.   for (i = 0; i < FD_SETSIZE; ++i)
  242.     if (FD_ISSET (i, &server_read))
  243.       {
  244.     if (i == server_sock)
  245.       {
  246.         /* Connection request on original socket. */
  247.         gint new;
  248.  
  249.         size = sizeof (clientname);
  250.         new = accept (server_sock,
  251.               (struct sockaddr *) &clientname,
  252.               &size);
  253.         if (new < 0)
  254.           {
  255.         perror ("accept");
  256.         return;
  257.           }
  258.  
  259.         /*  Associate the client address with the socket  */
  260.         g_hash_table_insert (clientname_ht,
  261.                  (gpointer) new,
  262.                  g_strdup (inet_ntoa (clientname.sin_addr)));
  263.         /*
  264.         server_log ("Server: connect from host %s, port %d.\n",
  265.             inet_ntoa (clientname.sin_addr),
  266.             (unsigned int) ntohs (clientname.sin_port));
  267.             */
  268.  
  269.         FD_SET (new, &server_active);
  270.       }
  271.     else
  272.       {
  273.         if (read_from_client (i) < 0)
  274.           {
  275.         /*  Disassociate the client address with the socket  */
  276.         g_hash_table_remove (clientname_ht, (gpointer) i);
  277.  
  278.         /*
  279.         server_log ("Server: disconnect from host %s, port %d.\n",
  280.                 inet_ntoa (clientname.sin_addr),
  281.                 (unsigned int) ntohs (clientname.sin_port));
  282.                 */
  283.  
  284.         close (i);
  285.         FD_CLR (i, &server_active);
  286.           }
  287.       }
  288.       }
  289. }
  290.  
  291. static void
  292. server_start (gint   port,
  293.           gchar *logfile)
  294. {
  295.   SFCommand *cmd;
  296.  
  297.   /*  Set up the clientname hash table  */
  298.   clientname_ht = g_hash_table_new (g_direct_hash, NULL);
  299.  
  300.   /*  Setup up the server log file  */
  301.   if (logfile)
  302.     server_log_file = fopen (logfile, "a");
  303.   else
  304.     server_log_file = NULL;
  305.   if (server_log_file == NULL)
  306.     server_log_file = stdout;
  307.  
  308.   /* Create the socket and set it up to accept connections. */
  309.   server_sock = make_socket (port);
  310.   if (listen (server_sock, 5) < 0)
  311.     {
  312.       perror ("listen");
  313.       return;
  314.     }
  315.  
  316.   server_log ("Script-fu initialized and listening...\n");
  317.  
  318.   /* Initialize the set of active sockets. */
  319.   FD_ZERO (&server_active);
  320.   FD_SET (server_sock, &server_active);
  321.  
  322.   /*  Loop until the server is finished  */
  323.   while (! script_fu_done)
  324.     {
  325.       script_fu_server_listen (0);
  326.  
  327.       while (command_queue)
  328.     {
  329.       /*  Get the current command  */
  330.       cmd = (SFCommand *) command_queue->data;
  331.  
  332.       /*  Process the command  */
  333.       execute_command (cmd);
  334.  
  335.       /*  Remove the command from the list  */
  336.       command_queue = g_list_remove (command_queue, cmd);
  337.       queue_length--;
  338.  
  339.       /*  Free the request  */
  340.       g_free (cmd->command);
  341.       g_free (cmd);
  342.     }
  343.     }
  344.  
  345.   server_quit ();
  346.  
  347.   /*  Close the server log file  */
  348.   if (server_log_file != stdout)
  349.     fclose (server_log_file);
  350. }
  351.  
  352. static gint
  353. execute_command (SFCommand *cmd)
  354. {
  355.   guchar buffer[RESPONSE_HEADER];
  356.   gchar *response;
  357.   time_t clock1, clock2;
  358.   gint response_len;
  359.   gint error;
  360.   gint i;
  361.  
  362.   /*  Get the client address from the address/socket table  */
  363.   server_log ("Processing request #%d\n", cmd->request_no);
  364.   time (&clock1);
  365.  
  366.   /*  run the command  */
  367.   if (repl_c_string (cmd->command, 0, 0, 1) != 0)
  368.     {
  369.       error = TRUE;
  370.       response_len = strlen (siod_err_msg);
  371.       response = siod_err_msg;
  372.  
  373.       server_log ("%s\n", siod_err_msg);
  374.     }
  375.   else
  376.     {
  377.       error = FALSE;
  378.  
  379.       if (TYPEP (repl_return_val, tc_string))
  380.     response = get_c_string (repl_return_val);
  381.       else
  382.     response = "Success";
  383.  
  384.       response_len = strlen (response);
  385.  
  386.       time (&clock2);
  387.       server_log ("Request #%d processed in %f seconds, finishing on %s",
  388.           cmd->request_no, difftime (clock2, clock1), ctime (&clock2));
  389.     }
  390.  
  391.   buffer[MAGIC_BYTE] = MAGIC;
  392.   buffer[ERROR] = (error) ? 1 : 0;
  393.   buffer[RSP_LEN_H_BYTE] = (guchar) (response_len >> 8);
  394.   buffer[RSP_LEN_L_BYTE] = (guchar) (response_len & 0xFF);
  395.  
  396.   /*  Write the response to the client  */
  397.   for (i = 0; i < RESPONSE_HEADER; i++)
  398.     if (write (cmd->filedes, buffer + i, 1) < 0)
  399.       {
  400.     /*  Write error  */
  401.     perror ("write");
  402.     return 0;
  403.       }
  404.  
  405.   for (i = 0; i < response_len; i++)
  406.     if (write (cmd->filedes, response + i, 1) < 0)
  407.       {
  408.     /*  Write error  */
  409.     perror ("write");
  410.     return 0;
  411.       }
  412.  
  413.   return 0;
  414. }
  415.  
  416. static gint
  417. read_from_client (gint filedes)
  418. {
  419.   SFCommand *cmd;
  420.   guchar buffer[COMMAND_HEADER];
  421.   gchar *command;
  422.   gchar *clientaddr;
  423.   time_t clock;
  424.   gint command_len;
  425.   gint nbytes;
  426.   gint i;
  427.  
  428.   for (i = 0; i < COMMAND_HEADER; i++)
  429.     {
  430.       if ((nbytes = read (filedes, buffer + i, 1)) < 0)
  431.     {
  432.       /* Read error. */
  433.       perror ("read");
  434.       return 0;
  435.     }
  436.       else if (nbytes == 0)
  437.     /* End-of-file. */
  438.     return -1;
  439.     }
  440.  
  441.   if (buffer[MAGIC_BYTE] != MAGIC)
  442.     {
  443.       server_log ("Error in script-fu command transmission.\n");
  444.       return -1;
  445.     }
  446.   command_len = (buffer [CMD_LEN_H_BYTE] << 8) | buffer [CMD_LEN_L_BYTE];
  447.   command = g_new (gchar, command_len + 1);
  448.  
  449.   for (i = 0; i < command_len; i++)
  450.     if (read (filedes, command + i, 1) == 0)
  451.       {
  452.     server_log ("Error reading command.  Read %d out of %d bytes.\n", i, command_len);
  453.     return -1;
  454.       }
  455.  
  456.   command[command_len] = '\0';
  457.   cmd = g_new (SFCommand, 1);
  458.   cmd->filedes = filedes;
  459.   cmd->command = command;
  460.   cmd->request_no = request_no ++;
  461.  
  462.   /*  Add the command to the queue  */
  463.   command_queue = g_list_append (command_queue, cmd);
  464.   queue_length ++;
  465.  
  466.   /*  Get the client address from the address/socket table  */
  467.   clientaddr = g_hash_table_lookup (clientname_ht, (gpointer) cmd->filedes);
  468.   time (&clock);
  469.   server_log ("Received request #%d from IP address %s: %s on %s, [Request queue length: %d]",
  470.           cmd->request_no, clientaddr, cmd->command, ctime (&clock), queue_length);
  471.  
  472.   return 0;
  473. }
  474.  
  475. static gint
  476. make_socket (guint port)
  477. {
  478.   gint sock;
  479.   struct sockaddr_in name;
  480.   gint v = 1;
  481.  
  482.   /* Create the socket. */
  483.   sock = socket (PF_INET, SOCK_STREAM, 0);
  484.   if (sock < 0)
  485.     {
  486.       perror ("socket");
  487.       gimp_quit ();
  488.     }
  489.   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
  490.  
  491.   /* Give the socket a name. */
  492.   name.sin_family = AF_INET;
  493.   name.sin_port = htons (port);
  494.   name.sin_addr.s_addr = htonl (INADDR_ANY);
  495.   if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
  496.     {
  497.       perror ("bind");
  498.       gimp_quit ();
  499.     }
  500.  
  501.   return sock;
  502. }
  503.  
  504. static void
  505. server_log (gchar *format, ...)
  506. {
  507.   va_list args;
  508.   char *buf;
  509.  
  510.   va_start (args, format);
  511.   buf = g_strdup_vprintf (format, args);
  512.   va_end (args);
  513.  
  514.   fputs (buf, server_log_file);
  515.   if (server_log_file != stdout)
  516.     fflush (server_log_file);
  517. }
  518.  
  519. static void
  520. server_quit (void)
  521. {
  522.   int i;
  523.  
  524.   for (i = 0; i < FD_SETSIZE; ++i)
  525.     if (FD_ISSET (i, &server_active))
  526.       shutdown (i, 2);
  527. }
  528.  
  529. static gint
  530. server_interface (void)
  531. {
  532.   GtkWidget *dlg;
  533.   GtkWidget *table;
  534.  
  535.   INIT_I18N_UI();
  536.  
  537.   gimp_ui_init ("script-fu", FALSE);
  538.  
  539.   dlg = gimp_dialog_new (_("Script-Fu Server Options"), "script-fu",
  540.              gimp_standard_help_func, "filters/script-fu.html", 
  541.              GTK_WIN_POS_MOUSE,
  542.              FALSE, TRUE, FALSE,
  543.  
  544.              _("OK"), ok_callback,
  545.              NULL, NULL, NULL, TRUE, FALSE,
  546.              _("Cancel"), gtk_widget_destroy,
  547.              NULL, 1, NULL, FALSE, TRUE,
  548.  
  549.              NULL);
  550.  
  551.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  552.               GTK_SIGNAL_FUNC (gtk_main_quit),
  553.               NULL);
  554.  
  555.   /*  The table to hold port & logfile entries  */
  556.   table = gtk_table_new (2, 2, FALSE);
  557.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  558.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  559.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  560.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, TRUE, TRUE, 0);
  561.  
  562.   /*  The server port  */
  563.   sint.port_entry = gtk_entry_new ();
  564.   gtk_entry_set_text (GTK_ENTRY (sint.port_entry), "10008");
  565.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, 
  566.                  _("Server Port:"), 1.0, 0.5,
  567.                  sint.port_entry, 1, TRUE);
  568.  
  569.   /*  The server logfile  */
  570.   sint.log_entry = gtk_entry_new ();
  571.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, 
  572.                  _("Server Logfile:"), 1.0, 0.5,
  573.                  sint.log_entry, 1, TRUE);
  574.  
  575.   gtk_widget_show (table);
  576.   gtk_widget_show (dlg);
  577.  
  578.   gtk_main ();
  579.   gdk_flush ();
  580.  
  581.   return sint.run;
  582. }
  583.  
  584. static void
  585. ok_callback (GtkWidget *widget,
  586.          gpointer   data)
  587. {
  588.   sint.port = atoi (gtk_entry_get_text (GTK_ENTRY (sint.port_entry)));
  589.   sint.logfile = g_strdup (gtk_entry_get_text (GTK_ENTRY (sint.log_entry)));
  590.   sint.run = TRUE;
  591.  
  592.   gtk_widget_destroy (GTK_WIDGET (data));
  593. }
  594.