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