6e908d107b92a05d37ee642e71a13b5b2bcb9f2d
[stwbeast.git] / sfi / sficomwire.c
1 /* SFI - Synthesis Fusion Kit Interface
2  * Copyright (C) 2002 Tim Janik
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * A copy of the GNU Lesser General Public License should ship along
15  * with this library; if not, see http://www.gnu.org/copyleft/.
16  */
17 #include "sficomwire.h"
18 #include "sfiprimitives.h"
19 #include <errno.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <signal.h>
24 #include <fcntl.h>
25 #include <sys/time.h>
26 #include <sys/resource.h>
27
28
29 /* --- prototypes --- */
30 static GList*   wire_find_link  (GList  *list,
31                                  guint   request);
32
33
34 /* --- functions --- */
35 static void
36 nonblock_fd (gint fd)
37 {
38   if (fd >= 0)
39     {
40       glong r, d_long;
41       do
42         d_long = fcntl (fd, F_GETFL);
43       while (d_long < 0 && errno == EINTR);
44       
45       d_long |= O_NONBLOCK;
46       
47       do
48         r = fcntl (fd, F_SETFL, d_long);
49       while (r < 0 && errno == EINTR);
50     }
51 }
52
53 SfiComWire*
54 sfi_com_wire_from_child (const gchar *ident,
55                          gint         remote_input,
56                          gint         remote_output,
57                          gint         standard_input,
58                          gint         standard_output,
59                          gint         standard_error,
60                          gint         remote_pid)
61 {
62   SfiComWire *wire;
63   
64   g_return_val_if_fail (ident != NULL, NULL);
65   
66   wire = g_new0 (SfiComWire, 1);
67   if (remote_pid > 1)
68     wire->ident = g_strdup_printf ("%s[%u]", ident, remote_pid);
69   else
70     wire->ident = g_strdup (ident);
71   wire->remote_input = remote_input;
72   wire->remote_output = remote_output;
73   wire->standard_input = standard_input;
74   wire->standard_output = standard_output;
75   wire->standard_error = standard_error;
76   wire->remote_pid = remote_pid > 1 ? remote_pid : -1;
77   wire->gstring_stdout = g_string_new ("");
78   wire->gstring_stderr = g_string_new ("");
79   wire->connected = (wire->remote_input >= 0 ||
80                      wire->remote_output >= 0 ||
81                      wire->standard_input >= 0 ||
82                      wire->standard_output >= 0 ||
83                      wire->standard_error >= 0);
84   sfi_com_wire_set_dispatcher (wire, NULL, NULL, NULL);
85   nonblock_fd (wire->remote_input);
86   nonblock_fd (wire->remote_output);
87   nonblock_fd (wire->standard_input);
88   nonblock_fd (wire->standard_output);
89   nonblock_fd (wire->standard_error);
90   
91   return wire;
92 }
93
94 SfiComWire*
95 sfi_com_wire_from_pipe (const gchar *ident,
96                         gint         remote_input,
97                         gint         remote_output)
98 {
99   g_return_val_if_fail (ident != NULL, NULL);
100   
101   return sfi_com_wire_from_child (ident,
102                                   remote_input,
103                                   remote_output,
104                                   -1, -1, -1, -1);
105 }
106
107 static SfiComMsg*
108 alloc_msg (SfiComMsgType type)
109 {
110   SfiComMsg *msg = g_new (SfiComMsg, 1);
111   
112   msg->magic = BSE_MAGIC_BSEm;
113   msg->mlength = 0;
114   msg->type = type;
115   
116   return msg;
117 }
118
119 static gchar*
120 free_msg_skel (SfiComMsg *msg)
121 {
122   gchar *content = msg->message;
123   
124   g_free (msg);
125   return content;
126 }
127
128 static void
129 free_msg (SfiComMsg *msg)
130 {
131   g_free (free_msg_skel (msg));
132 }
133
134 static void
135 wire_write_remote (SfiComWire *wire)
136 {
137   guint8 *buf = wire->obuffer;
138   
139   if (wire->obp - buf && wire->remote_output >= 0)
140     {
141       gint n;
142       
143       do
144         {
145           n = write (wire->remote_output, buf, wire->obp - buf);
146           buf += MAX (n, 0);
147         }
148       while (n < 0 && errno == EINTR);
149       
150       if (n == 0 || (n < 0 && errno != EINTR && errno != EAGAIN))
151         wire->remote_output_broke = TRUE;
152       
153       n = wire->obp - buf;
154       g_memmove (wire->obuffer, buf, n);
155       wire->obp = wire->obuffer + n;
156     }
157 }
158
159 static inline gpointer
160 put_uint32 (gpointer p,
161             guint32  val)
162 {
163   guint32 *ip = p;
164   
165   *ip++ = GUINT32_TO_BE (val);
166   return ip;
167 }
168
169 static void
170 wire_send (SfiComWire *wire,
171            SfiComMsg  *msg)
172 {
173   guint strl;
174   
175   g_return_if_fail (msg->mlength == 0);
176   
177   strl = strlen (msg->message) + 1;     /* include trailing 0 */
178   msg->mlength = (4 +   /* magic */
179                   4 +   /* mlength */
180                   4 +   /* type */
181                   4 +   /* request */
182                   strl);
183   if (wire->obp + msg->mlength >= wire->obound)
184     {
185       guint l = wire->obp - wire->obuffer;
186       wire->obuffer = g_renew (guint8, wire->obuffer, l + msg->mlength);
187       wire->obp = wire->obuffer + l;
188       wire->obound = wire->obp + msg->mlength;
189     }
190   wire->obp = put_uint32 (wire->obp, msg->magic);
191   wire->obp = put_uint32 (wire->obp, msg->mlength);
192   wire->obp = put_uint32 (wire->obp, msg->type);
193   wire->obp = put_uint32 (wire->obp, msg->request);
194   memcpy (wire->obp, msg->message, strl);
195   wire->obp += strl;
196   wire_write_remote (wire);
197 }
198
199 static void
200 wire_read_remote (SfiComWire *wire)
201 {
202   if (wire->remote_input >= 0)
203     {
204       guint read_size = 8192;
205       gint n;
206       
207       if (wire->ibound - wire->ibp < read_size)
208         {
209           guint l = wire->ibp - wire->ibuffer;
210           wire->ibuffer = g_renew (guint8, wire->ibuffer, l + read_size);
211           wire->ibp = wire->ibuffer + l;
212           wire->ibound = wire->ibp + read_size;
213         }
214       
215       do
216         {
217           n = read (wire->remote_input, wire->ibp, wire->ibound - wire->ibp);
218           wire->ibp += MAX (n, 0);
219         }
220       while (n < 0 && errno == EINTR);
221       
222       /* n==0 on pipes/fifos means remote closed the connection (end-of-file) */
223       if (n == 0 || (n < 0 && errno != EINTR && errno != EAGAIN))
224         wire->remote_input_broke = TRUE;
225     }
226 }
227
228 static inline gpointer
229 get_uint32 (gpointer p,
230             guint32 *val)
231 {
232   guint32 *ip = p, v;
233   
234   v = *ip++;
235   *val = GUINT32_FROM_BE (v);
236   return ip;
237 }
238
239 static void
240 wire_receive (SfiComWire *wire)
241 {
242   wire_read_remote (wire);
243   
244   if (wire->ibp >= wire->ibuffer + 4 + 4 + 4)   /* magic + mlength + type */
245     {
246       guint8 *p = wire->ibuffer;
247       guint32 magic, mlength, type;
248       guint mheader_length = 4 + 4 + 4 + 4, max_mlength = 4 * 1024 * 1024;
249       
250       p = get_uint32 (p, &magic);
251       p = get_uint32 (p, &mlength);
252       p = get_uint32 (p, &type);
253       if (magic != BSE_MAGIC_BSEm)
254         {
255           g_printerr ("%s: message with invalid magic: 0x%08x\n", wire->ident, magic);
256           wire->remote_input_broke = TRUE;
257           wire->ibp = wire->ibuffer;
258         }
259       else if (mlength <= mheader_length || mlength >= max_mlength)
260         {
261           g_printerr ("%s: message (type=%u) with invalid length: %u < %u < %u\n",
262                       wire->ident, type, mheader_length, mlength, max_mlength);
263           wire->remote_input_broke = TRUE;
264           wire->ibp = wire->ibuffer;
265         }
266       else if (mlength <= wire->ibp - wire->ibuffer)
267         {
268           guint strl = mlength - mheader_length;        /* >= 1 */
269           
270           switch (type)
271             {
272               SfiComMsg *msg;
273               guint n;
274             case SFI_COM_MSG_REQUEST:
275             case SFI_COM_MSG_RESULT:
276               msg = alloc_msg (type);
277               msg->mlength = mlength;
278               p = get_uint32 (p, &msg->request);
279               msg->message = g_new (gchar, strl);
280               memcpy (msg->message, p, strl - 1);       /* ignoring trailing 0 */
281               msg->message[strl - 1] = 0;
282               p += strl;
283               if (type == SFI_COM_MSG_REQUEST)
284                 wire->irequests = g_list_append (wire->irequests, msg);
285               else
286                 {
287                   if (wire_find_link (wire->orequests, msg->request))
288                     wire->iresults = g_list_prepend (wire->iresults, msg);
289                   else
290                     {
291                       g_printerr ("%s: ignoring spurious result (request=%u): %s\n", wire->ident, msg->request, msg->message);
292                       free_msg (msg);
293                     }
294                 }
295               n = wire->ibp - p;
296               g_memmove (wire->ibuffer, p, n);
297               wire->ibp = wire->ibuffer + n;
298               break;
299             case SFI_COM_MSG_RESERVED1:
300             case SFI_COM_MSG_RESERVED2:
301             case SFI_COM_MSG_RESERVED3:
302             case SFI_COM_MSG_RESERVED4:
303               g_printerr ("%s: ignoring message with unknown type: %u\n",
304                           wire->ident, type);
305               p += 4;   /* request */
306               p += strl;
307               n = wire->ibp - p;
308               g_memmove (wire->ibuffer, p, n);
309               wire->ibp = wire->ibuffer + n;
310               break;
311             default:
312               g_printerr ("%s: message with invalid type: %u\n",
313                           wire->ident, type);
314               wire->remote_input_broke = TRUE;
315               wire->ibp = wire->ibuffer;
316               break;
317             }
318         }
319     }
320 }
321
322 static inline gboolean  /* returns: connection_alive */
323 wire_read_gstring (SfiComWire *wire,
324                    gint        fd,
325                    GString    *gstring)
326 {
327   guint l = gstring->len;
328   guint8 *pos, *bound;
329   gint n;
330   
331   g_string_set_size (gstring, l + 8192);
332   pos = gstring->str + l;
333   bound = gstring->str + gstring->len;
334   do
335     {
336       n = read (fd, pos, bound - pos);
337       pos += MAX (n, 0);
338     }
339   while (n < 0 && errno == EINTR);
340   g_string_set_size (gstring, pos - (guint8*) gstring->str);
341   
342   /* n==0 on pipes/fifos means remote closed the connection (end-of-file) */
343   return n > 0 || (n < 0 && (errno == EINTR || errno == EAGAIN));
344 }
345
346 static void
347 wire_capture (SfiComWire *wire)
348 {
349   if (wire->standard_output >= 0)
350     if (!wire_read_gstring (wire, wire->standard_output, wire->gstring_stdout))
351       wire->standard_output_broke = TRUE;
352   if (wire->standard_error >= 0)
353     if (!wire_read_gstring (wire, wire->standard_error, wire->gstring_stderr))
354       wire->standard_error_broke = TRUE;
355 }
356
357 static inline void
358 wire_update_alive (SfiComWire *wire)
359 {
360   if (wire->remote_input_broke ||
361       wire->remote_output_broke ||
362       wire->standard_input_broke ||
363       wire->standard_output_broke ||
364       wire->standard_error_broke)
365     wire->connected = FALSE;
366 }
367
368 static GList*
369 wire_find_link (GList *list,
370                 guint  request)
371 {
372   for (; list; list = list->next)
373     {
374       SfiComMsg *msg = list->data;
375       
376       if (msg->request == request)
377         return list;
378     }
379   return NULL;
380 }
381
382 static guint
383 wire_alloc_request (SfiComWire *wire)
384 {
385   guint request = (rand () << 16) ^ rand ();
386   
387   while (request == 0 || wire_find_link (wire->orequests, request))
388     request++;
389   
390   return request;
391 }
392
393 guint
394 sfi_com_wire_send_request (SfiComWire  *wire,
395                            const gchar *request_msg)
396 {
397   SfiComMsg *msg;
398   guint request;
399   
400   g_return_val_if_fail (wire != NULL, 0);
401   g_return_val_if_fail (request_msg != NULL, 0);
402   
403   request = wire_alloc_request (wire);
404   msg = alloc_msg (SFI_COM_MSG_REQUEST);
405   msg->request = request;
406   msg->message = g_strdup (request_msg);
407   
408   wire->orequests = g_list_prepend (wire->orequests, msg);
409   wire_send (wire, msg);
410   
411   wire_update_alive (wire);
412   
413   return request;
414 }
415
416 gchar*
417 sfi_com_wire_receive_result (SfiComWire *wire,
418                              guint       request)
419 {
420   GList *out_link, *in_link;
421   
422   g_return_val_if_fail (wire != NULL, NULL);
423   g_return_val_if_fail (request > 0, NULL);
424   out_link = wire_find_link (wire->orequests, request);
425   g_return_val_if_fail (out_link != NULL, NULL);
426   
427   wire_receive (wire);
428   wire_update_alive (wire);
429   
430   in_link = wire_find_link (wire->iresults, request);
431   if (in_link)
432     {
433       SfiComMsg *omsg = out_link->data;
434       SfiComMsg *imsg = in_link->data;
435       
436       wire->orequests = g_list_delete_link (wire->orequests, out_link);
437       wire->iresults = g_list_delete_link (wire->iresults, in_link);
438       free_msg (omsg);
439       return free_msg_skel (imsg);
440     }
441   else
442     return NULL;
443 }
444
445 void
446 sfi_com_wire_forget_request (SfiComWire *wire,
447                              guint       request)
448 {
449   GList *out_link;
450   SfiComMsg *omsg;
451   
452   g_return_if_fail (wire != NULL);
453   g_return_if_fail (request > 0);
454   out_link = wire_find_link (wire->orequests, request);
455   g_return_if_fail (out_link != NULL);
456   
457   omsg = out_link->data;
458   wire->orequests = g_list_delete_link (wire->orequests, out_link);
459   free_msg (omsg);
460 }
461
462 guint
463 sfi_com_wire_peek_first_result (SfiComWire *wire)
464 {
465   SfiComMsg *msg;
466   
467   g_return_val_if_fail (wire != NULL, 0);
468   
469   msg = wire->iresults ? wire->iresults->data : NULL;
470   return msg ? msg->request : 0;
471 }
472
473 const gchar*
474 sfi_com_wire_receive_request (SfiComWire *wire,
475                               guint      *request_p)
476 {
477   g_return_val_if_fail (wire != NULL, NULL);
478   g_return_val_if_fail (request_p != NULL, NULL);
479   
480   wire_receive (wire);
481   wire_update_alive (wire);
482   
483   if (wire->irequests)
484     {
485       SfiComMsg *msg = wire->irequests->data;
486       
487       wire->irequests = g_list_remove (wire->irequests, msg);
488       if (msg->request == 0)
489         {
490           /* 0-requests are low-level messages, currently unhandled */
491           g_printerr ("%s: ignoring message with request_id=0\n", wire->ident);
492           free_msg (msg);
493           return sfi_com_wire_receive_request (wire, request_p);
494         }
495       wire->rrequests = g_list_prepend (wire->rrequests, msg);
496       *request_p = msg->request;
497       
498       return msg->message;
499     }
500   else
501     {
502       *request_p = 0;
503       return NULL;
504     }
505 }
506
507 void
508 sfi_com_wire_send_result (SfiComWire  *wire,
509                           guint        request,
510                           const gchar *result_msg)
511 {
512   SfiComMsg *msg;
513   GList *received_link;
514   
515   g_return_if_fail (wire != NULL);
516   g_return_if_fail (request > 0);
517   g_return_if_fail (result_msg != NULL);
518   received_link = wire_find_link (wire->rrequests, request);
519   g_return_if_fail (received_link != NULL);
520   
521   msg = alloc_msg (SFI_COM_MSG_RESULT);
522   msg->request = request;
523   msg->message = g_strdup (result_msg);
524   wire_send (wire, msg);
525   
526   free_msg (received_link->data);
527   wire->rrequests = g_list_delete_link (wire->rrequests, received_link);
528   free_msg (msg);
529   
530   wire_update_alive (wire);
531 }
532
533 void
534 sfi_com_wire_discard_request (SfiComWire *wire,
535                               guint       request)
536 {
537   GList *received_link;
538   
539   g_return_if_fail (wire != NULL);
540   g_return_if_fail (request > 0);
541   received_link = wire_find_link (wire->rrequests, request);
542   g_return_if_fail (received_link != NULL);
543   
544   free_msg (received_link->data);
545   wire->rrequests = g_list_delete_link (wire->rrequests, received_link);
546   
547   wire_update_alive (wire);
548 }
549
550 static gboolean
551 wire_default_dispatch (gpointer     data,
552                        guint        request,
553                        const gchar *request_msg,
554                        SfiComWire  *wire)
555 {
556   g_printerr ("%s: unhandled request (id=%u): %s\n", wire->ident, request, request_msg);
557   sfi_com_wire_discard_request (wire, request);
558   return TRUE;
559 }
560
561 void
562 sfi_com_wire_set_dispatcher (SfiComWire    *wire,
563                              SfiComDispatch dispatch_func,
564                              gpointer       dispatch_data,
565                              GDestroyNotify destroy_data)
566 {
567   g_return_if_fail (wire != NULL);
568   
569   if (wire->destroy_data)
570     wire->destroy_data (wire->dispatch_data);
571   if (dispatch_func)
572     {
573       wire->dispatch_func = dispatch_func;
574       wire->dispatch_data = dispatch_data;
575       wire->destroy_data = destroy_data;
576     }
577   else
578     {
579       wire->dispatch_func = wire_default_dispatch;
580       wire->dispatch_data = NULL;
581       wire->destroy_data = NULL;
582     }
583 }
584
585 void
586 sfi_com_wire_dispatch (SfiComWire  *wire,
587                        guint        request)
588 {
589   GList *received_link;
590   SfiComMsg *msg;
591   gboolean handled;
592   
593   g_return_if_fail (wire != NULL);
594   g_return_if_fail (request > 0);
595   received_link = wire_find_link (wire->rrequests, request);
596   g_return_if_fail (received_link != NULL);
597   
598   msg = received_link->data;
599   handled = wire->dispatch_func (wire->dispatch_data, msg->request, msg->message, wire);
600   if (!handled)
601     wire_default_dispatch (NULL, msg->request, msg->message, wire);
602 }
603
604 gboolean
605 sfi_com_wire_need_dispatch (SfiComWire *wire)
606 {
607   g_return_val_if_fail (wire != NULL, FALSE);
608   
609   return wire->iresults || wire->irequests || wire->gstring_stdout->len || wire->gstring_stderr->len;
610 }
611
612 gint*
613 sfi_com_wire_get_read_fds (SfiComWire *wire,
614                            guint      *n_fds_p)
615 {
616   g_return_val_if_fail (wire != NULL, NULL);
617   g_return_val_if_fail (n_fds_p != NULL, NULL);
618   
619   if (wire->remote_input >= 0 ||
620       wire->standard_output >= 0 ||
621       wire->standard_error >= 0)
622     {
623       guint n_fds = 0;
624       gint *fds = g_new (gint, 3);
625       
626       if (wire->remote_input >= 0)
627         fds[n_fds++] = wire->remote_input;
628       if (wire->standard_output >= 0)
629         fds[n_fds++] = wire->standard_output;
630       if (wire->standard_error >= 0)
631         fds[n_fds++] = wire->standard_error;
632       *n_fds_p = n_fds;
633       return fds;
634     }
635   else
636     {
637       *n_fds_p = 0;
638       return NULL;
639     }
640 }
641
642 gint*
643 sfi_com_wire_get_write_fds (SfiComWire *wire,
644                             guint      *n_fds_p)
645 {
646   g_return_val_if_fail (wire != NULL, NULL);
647   g_return_val_if_fail (n_fds_p != NULL, NULL);
648   
649   if (wire->obp - wire->obuffer && wire->remote_output >= 0)
650     {
651       guint n_fds = 0;
652       gint *fds = g_new (gint, 1);
653       
654       fds[n_fds++] = wire->remote_output;
655       *n_fds_p = n_fds;
656       return fds;
657     }
658   else
659     {
660       *n_fds_p = 0;
661       return NULL;
662     }
663 }
664
665 GPollFD*
666 sfi_com_wire_get_poll_fds (SfiComWire *wire,
667                            guint      *n_pfds_p)
668 {
669   g_return_val_if_fail (wire != NULL, NULL);
670   g_return_val_if_fail (n_pfds_p != NULL, NULL);
671   
672   if (wire->remote_input >= 0 ||
673       wire->standard_output >= 0 ||
674       wire->standard_error >= 0 ||
675       wire->remote_output >= 0)
676     {
677       guint n_pfds = 0;
678       GPollFD *pfds = g_new0 (GPollFD, 3 + 1);
679       
680       if (wire->remote_input >= 0)
681         {
682           pfds[n_pfds].fd = wire->remote_input;
683           pfds[n_pfds++].events = G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
684         }
685       if (wire->standard_output >= 0)
686         {
687           pfds[n_pfds].fd = wire->standard_output;
688           pfds[n_pfds++].events = G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
689         }
690       if (wire->standard_error >= 0)
691         {
692           pfds[n_pfds].fd = wire->standard_error;
693           pfds[n_pfds++].events = G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
694         }
695       if (wire->remote_output >= 0)
696         {
697           pfds[n_pfds].fd = wire->remote_output;
698           pfds[n_pfds].events = G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
699           if (wire->obp - wire->obuffer)
700             pfds[n_pfds].events |= G_IO_OUT;
701           n_pfds++;
702         }
703       *n_pfds_p = n_pfds;
704       return pfds;
705     }
706   else
707     {
708       *n_pfds_p = 0;
709       return NULL;
710     }
711 }
712
713 void
714 sfi_com_wire_process_io (SfiComWire *wire)
715 {
716   g_return_if_fail (wire != NULL);
717   
718   wire_capture (wire);
719   wire_write_remote (wire);
720   wire_read_remote (wire);
721   wire_capture (wire);
722   
723   if (wire->remote_input_broke)
724     {
725       if (wire->remote_input >= 0)
726         close (wire->remote_input);
727       wire->remote_input = -1;
728     }
729   if (wire->remote_output_broke)
730     {
731       if (wire->remote_output >= 0)
732         close (wire->remote_output);
733       wire->remote_output = -1;
734     }
735   if (wire->standard_input_broke)
736     {
737       if (wire->standard_input >= 0)
738         close (wire->standard_input);
739       wire->standard_input = -1;
740     }
741   if (wire->standard_output_broke)
742     {
743       if (wire->standard_output >= 0)
744         close (wire->standard_output);
745       wire->standard_output = -1;
746     }
747   if (wire->standard_error_broke)
748     {
749       if (wire->standard_error >= 0)
750         close (wire->standard_error);
751       wire->standard_error = -1;
752     }
753 }
754
755 void
756 sfi_com_wire_close_remote (SfiComWire *wire,
757                            gboolean    terminate)
758 {
759   g_return_if_fail (wire != NULL);
760   
761   wire->connected = FALSE;
762   if (wire->remote_input >= 0)
763     close (wire->remote_input);
764   wire->remote_input = -1;
765   if (wire->remote_output >= 0)
766     close (wire->remote_output);
767   wire->remote_output = -1;
768   if (wire->standard_input >= 0)
769     close (wire->standard_input);
770   wire->standard_input = -1;
771   if (wire->standard_output >= 0)
772     close (wire->standard_output);
773   wire->standard_output = -1;
774   if (wire->standard_error >= 0)
775     close (wire->standard_error);
776   wire->standard_error = -1;
777   if (wire->remote_pid > 1 && terminate)
778     kill (wire->remote_pid, SIGTERM);
779   wire->remote_pid = -1;
780 }
781
782 void
783 sfi_com_wire_destroy (SfiComWire *wire)
784 {
785   GList *list;
786   
787   g_return_if_fail (wire != NULL);
788   
789   sfi_com_wire_set_dispatcher (wire, NULL, NULL, NULL);
790   sfi_com_wire_close_remote (wire, TRUE);
791   for (list = wire->orequests; list; list = list->next)
792     free_msg (list->data);
793   g_list_free (wire->orequests);
794   for (list = wire->iresults; list; list = list->next)
795     free_msg (list->data);
796   g_list_free (wire->iresults);
797   for (list = wire->irequests; list; list = list->next)
798     free_msg (list->data);
799   g_list_free (wire->irequests);
800   for (list = wire->rrequests; list; list = list->next)
801     free_msg (list->data);
802   g_list_free (wire->rrequests);
803   g_string_free (wire->gstring_stdout, TRUE);
804   g_string_free (wire->gstring_stderr, TRUE);
805   g_free (wire->ibuffer);
806   g_free (wire->obuffer);
807   g_free (wire->ident);
808   g_free (wire);
809 }
810
811 gboolean
812 sfi_com_wire_receive_dispatch (SfiComWire *wire)
813 {
814   guint request;
815   
816   g_return_val_if_fail (wire != NULL, FALSE);
817   
818   if (sfi_com_wire_receive_request (wire, &request))
819     {
820       sfi_com_wire_dispatch (wire, request);
821       return TRUE;
822     }
823   else
824     return FALSE;
825 }
826
827 void
828 sfi_com_wire_select (SfiComWire *wire,
829                      guint       timeout)
830 {
831   fd_set rfds, wfds, efds;
832   guint *fds, i, n, max_fd = 0;
833   struct timeval tv;
834   
835   g_return_if_fail (wire != NULL);
836   
837   FD_ZERO (&rfds);
838   FD_ZERO (&wfds);
839   FD_ZERO (&efds);
840   
841   fds = sfi_com_wire_get_read_fds (wire, &n);
842   for (i = 0; i < n; i++)
843     {
844       FD_SET (fds[i], &rfds);
845       FD_SET (fds[i], &efds);
846       max_fd = MAX (max_fd, fds[i]);
847     }
848   g_free (fds);
849   
850   fds = sfi_com_wire_get_write_fds (wire, &n);
851   for (i = 0; i < n; i++)
852     {
853       FD_SET (fds[i], &wfds);
854       FD_SET (fds[i], &efds);
855       max_fd = MAX (max_fd, fds[i]);
856     }
857   g_free (fds);
858   
859   tv.tv_usec = (timeout % 1000) * 1000;
860   tv.tv_sec = timeout / 1000;
861   select (max_fd + 1, &rfds, &wfds, NULL, &tv);
862 }
863
864 gchar*
865 sfi_com_wire_ping_pong (SfiComWire  *wire,
866                         const gchar *ping,
867                         guint        timeout)
868 {
869   guint request;
870   gchar *pong;
871   
872   g_return_val_if_fail (wire != NULL, NULL);
873   g_return_val_if_fail (ping != NULL, NULL);
874   
875   request = sfi_com_wire_send_request (wire, ping);
876   pong = sfi_com_wire_receive_result (wire, request);
877   if (pong)
878     return pong;
879   
880   sfi_com_wire_select (wire, timeout / 4);
881   sfi_com_wire_process_io (wire);
882   pong = sfi_com_wire_receive_result (wire, request);
883   if (pong)
884     return pong;
885   
886   sfi_com_wire_select (wire, timeout / 4);
887   sfi_com_wire_process_io (wire);
888   pong = sfi_com_wire_receive_result (wire, request);
889   if (pong)
890     return pong;
891   
892   sfi_com_wire_select (wire, timeout / 4);
893   sfi_com_wire_process_io (wire);
894   pong = sfi_com_wire_receive_result (wire, request);
895   if (pong)
896     return pong;
897   
898   sfi_com_wire_select (wire, timeout / 4);
899   sfi_com_wire_process_io (wire);
900   pong = sfi_com_wire_receive_result (wire, request);
901   if (pong)
902     return pong;
903   
904   sfi_com_wire_forget_request (wire, request);
905   return NULL;
906 }
907
908
909 /* --- fork/exec --- */
910 static gchar *spawn_current_dir = NULL;
911
912 void
913 sfi_com_set_spawn_dir (const gchar *cwd)
914 {
915   g_free (spawn_current_dir);
916   spawn_current_dir = g_strdup (cwd);
917 }
918
919 static void
920 unset_cloexec (gint fd)
921 {
922   gint r;
923   
924   do
925     r = fcntl (fd, F_SETFD, 0 /* FD_CLOEXEC */);
926   while (r < 0 && errno == EINTR);
927 }
928
929 typedef struct {
930   gint keepexec1;
931   gint keepexec2;
932 } ChildSetupData;
933
934 static void
935 pre_exec_child_setup (gpointer data)
936 {
937   ChildSetupData *cdata = data;
938   
939   if (cdata->keepexec1)
940     unset_cloexec (cdata->keepexec1);
941   if (cdata->keepexec2)
942     unset_cloexec (cdata->keepexec2);
943   /* drop scheduling priorities if we have any */
944   setpriority (PRIO_PROCESS, getpid(), 0);
945 }
946
947 gchar*
948 sfi_com_spawn_async (const gchar *executable,
949                      gint        *child_pid,
950                      gint        *standard_input,       /* writable */
951                      gint        *standard_output,      /* readable */
952                      gint        *standard_error,       /* readable */
953                      const gchar *command_fd_option,
954                      gint        *command_input,        /* writable */
955                      gint        *command_output,       /* readable */
956                      SfiRing     *args)
957 {
958   gint command_input_pipe[2] = { -1, -1 };
959   gint command_output_pipe[2] = { -1, -1 };
960   ChildSetupData setup_data = { -1, -1 };
961   SfiRing *ring, *cargs = NULL;
962   gchar **argv, **argp, *reterr = NULL;
963   GError *error = NULL;
964   guint l;
965   
966   g_return_val_if_fail (executable != NULL, NULL);
967   if (command_fd_option)
968     g_return_val_if_fail (command_fd_option && command_input && command_output, NULL);
969   else
970     g_return_val_if_fail (!command_fd_option && !command_input && !command_output, NULL);
971   
972   if (command_fd_option)
973     {
974       if (pipe (command_output_pipe) < 0 || pipe (command_input_pipe) < 0)
975         {
976           gint e = errno;
977           
978           if (command_output_pipe[0] >= 0)
979             {
980               close (command_output_pipe[0]);
981               close (command_output_pipe[1]);
982             }
983           
984           return g_strdup_printf ("failed to create communication channels: %s", g_strerror (e));
985         }
986       cargs = sfi_ring_prepend (cargs, g_strdup_printf ("%u", command_output_pipe[1]));
987       cargs = sfi_ring_prepend (cargs, g_strdup_printf ("%u", command_input_pipe[0]));
988       if (command_fd_option[0])
989         cargs = sfi_ring_prepend (cargs, g_strdup (command_fd_option));
990       setup_data.keepexec1 = command_output_pipe[1];
991       setup_data.keepexec2 = command_input_pipe[0];
992     }
993   cargs = sfi_ring_prepend (cargs, g_strdup_printf (/*"SFI-Spawn:%s"*/"%s", executable));
994   cargs = sfi_ring_prepend (cargs, g_strdup (executable));
995   
996   l = sfi_ring_length (cargs) + sfi_ring_length (args);
997   argv = g_new (gchar*, l + 1);
998   argp = argv;
999   for (ring = cargs; ring; ring = sfi_ring_walk (ring, cargs))
1000     *argp++ = ring->data;
1001   for (ring = args; ring; ring = sfi_ring_walk (ring, args))
1002     *argp++ = ring->data;
1003   *argp = NULL;
1004   
1005   if (!g_spawn_async_with_pipes (spawn_current_dir, argv, NULL,
1006                                  G_SPAWN_DO_NOT_REAP_CHILD |
1007                                  /* G_SPAWN_CHILD_INHERITS_STDIN | */
1008                                  G_SPAWN_FILE_AND_ARGV_ZERO,
1009                                  pre_exec_child_setup, &setup_data,
1010                                  child_pid,
1011                                  standard_input,
1012                                  standard_output,
1013                                  standard_error,
1014                                  &error))
1015     {
1016       reterr = error ? error->message : "failed to spawn child process";
1017       reterr = g_strdup (reterr);
1018       g_clear_error (&error);
1019       close (command_output_pipe[0]);   command_output_pipe[0] = -1;
1020       close (command_output_pipe[1]);   command_output_pipe[1] = -1;
1021       close (command_input_pipe[0]);    command_input_pipe[0] = -1;
1022       close (command_input_pipe[1]);    command_input_pipe[1] = -1;
1023       if (child_pid)
1024         *child_pid = 0;
1025       if (standard_input)
1026         *standard_input = -1;
1027       if (standard_output)
1028         *standard_output = -1;
1029       if (standard_error)
1030         *standard_error = -1;
1031       goto cleanup;
1032     }
1033   
1034  cleanup:
1035   g_free (argv);
1036   for (ring = cargs; ring; ring = sfi_ring_walk (ring, cargs))
1037     g_free (ring->data);
1038   sfi_ring_free (cargs);
1039   if (command_fd_option)
1040     {
1041       if (command_output_pipe[1] >= 0)
1042         {
1043           close (command_output_pipe[1]);
1044           close (command_input_pipe[0]);
1045         }
1046       *command_input = command_input_pipe[1];
1047       *command_output = command_output_pipe[0];
1048     }
1049   
1050   return reterr;
1051 }