b9172ca41950fa716235833f7e2b0c2853fbd957
[debian/amanda] / common-src / util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $
28  */
29
30 #include "amanda.h"
31 #include "util.h"
32 #include "match.h"
33 #include <regex.h>
34 #include "arglist.h"
35 #include "clock.h"
36 #include "sockaddr-util.h"
37 #include "conffile.h"
38 #include "base64.h"
39 #include "stream.h"
40 #include "pipespawn.h"
41 #include <glib.h>
42 #include <string.h>
43
44 static int make_socket(sa_family_t family);
45 static int connect_port(sockaddr_union *addrp, in_port_t port, char *proto,
46                         sockaddr_union *svaddr, int nonblock);
47
48 static int
49 make_socket(
50     sa_family_t family)
51 {
52     int s;
53     int save_errno;
54 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
55     int on=1;
56     int r;
57 #endif
58
59     g_debug("make_socket opening socket with family %d", family);
60     s = socket(family, SOCK_STREAM, 0);
61     if (s == -1) {
62         save_errno = errno;
63         dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno));
64         errno = save_errno;
65         return -1;
66     }
67     if (s < 0 || s >= (int)FD_SETSIZE) {
68         aclose(s);
69         errno = EMFILE;                         /* out of range */
70         return -1;
71     }
72
73 #ifdef USE_REUSEADDR
74     r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
75     if (r < 0) {
76         save_errno = errno;
77         dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"),
78                   strerror(errno));
79         errno = save_errno;
80     }
81 #endif
82
83 #ifdef SO_KEEPALIVE
84     r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
85                    (void *)&on, SIZEOF(on));
86     if (r == -1) {
87         save_errno = errno;
88         dbprintf(_("make_socket: setsockopt() failed: %s\n"),
89                   strerror(save_errno));
90         aclose(s);
91         errno = save_errno;
92         return -1;
93     }
94 #endif
95
96     return s;
97 }
98
99 GQuark am_util_error_quark(void)
100 {
101     return g_quark_from_static_string("am-util-error-quark");
102 }
103
104 /* addrp is my address */
105 /* svaddr is the address of the remote machine */
106 /* return socket on success */
107 /* return -1     on failure */
108 int
109 connect_portrange(
110     sockaddr_union *addrp,
111     in_port_t           first_port,
112     in_port_t           last_port,
113     char *              proto,
114     sockaddr_union *svaddr,
115     int                 nonblock)
116 {
117     int                 s;
118     in_port_t           port;
119     static in_port_t    port_in_use[1024];
120     static int          nb_port_in_use = 0;
121     int                 i;
122     int                 save_errno = EAGAIN;
123
124     assert(first_port <= last_port);
125     /* Try a port already used */
126     for(i=0; i < nb_port_in_use; i++) {
127         port = port_in_use[i];
128         if(port >= first_port && port <= last_port) {
129             s = connect_port(addrp, port, proto, svaddr, nonblock);
130             if(s == -2) return -1;
131             if(s > 0) {
132                 return s;
133             }
134             if (errno != EAGAIN && errno != EBUSY)
135                 save_errno = errno;
136         }
137     }
138
139     /* Try a port in the range */
140     for (port = first_port; port <= last_port; port++) {
141         s = connect_port(addrp, port, proto, svaddr, nonblock);
142         if(s == -2) return -1;
143         if(s > 0) {
144             port_in_use[nb_port_in_use++] = port;
145             return s;
146         }
147         if (errno != EAGAIN && errno != EBUSY)
148             save_errno = errno;
149     }
150
151     dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
152               first_port,
153               last_port);
154     errno = save_errno;
155     return -1;
156 }
157
158 /* addrp is my address */
159 /* svaddr is the address of the remote machine */
160 /* return -2: Don't try again */
161 /* return -1: Try with another port */
162 /* return >0: this is the connected socket */
163 int
164 connect_port(
165     sockaddr_union *addrp,
166     in_port_t           port,
167     char *              proto,
168     sockaddr_union *svaddr,
169     int                 nonblock)
170 {
171     int                 save_errno;
172     struct servent *    servPort;
173     socklen_t_equiv     len;
174     socklen_t_equiv     socklen;
175     int                 s;
176
177     servPort = getservbyport((int)htons(port), proto);
178     if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
179         dbprintf(_("connect_port: Skip port %d: owned by %s.\n"),
180                   port, servPort->s_name);
181         errno = EBUSY;
182         return -1;
183     }
184
185     if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2;
186
187     SU_SET_PORT(addrp, port);
188     socklen = SS_LEN(addrp);
189     if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
190         save_errno = errno;
191         aclose(s);
192         if(servPort == NULL) {
193             dbprintf(_("connect_port: Try  port %d: available - %s\n"),
194                      port, strerror(errno));
195         } else {
196             dbprintf(_("connect_port: Try  port %d: owned by %s - %s\n"),
197                      port, servPort->s_name, strerror(errno));
198         }
199         if (save_errno != EADDRINUSE) {
200             errno = save_errno;
201             return -2;
202         }
203
204         errno = save_errno;
205         return -1;
206     }
207     if(servPort == NULL) {
208         dbprintf(_("connect_port: Try  port %d: available - Success\n"), port);
209     } else {
210         dbprintf(_("connect_port: Try  port %d: owned by %s - Success\n"),
211                   port, servPort->s_name);
212     }
213
214     /* find out what port was actually used */
215
216     len = sizeof(*addrp);
217     if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
218         save_errno = errno;
219         dbprintf(_("connect_port: getsockname() failed: %s\n"),
220                   strerror(save_errno));
221         aclose(s);
222         errno = save_errno;
223         return -1;
224     }
225
226     if (nonblock)
227         fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
228     if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
229         save_errno = errno;
230         dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
231                   str_sockaddr(addrp),
232                   strerror(save_errno));
233         dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
234                   str_sockaddr(svaddr),
235                   strerror(save_errno));
236         aclose(s);
237         errno = save_errno;
238         if (save_errno == ECONNREFUSED ||
239             save_errno == EHOSTUNREACH ||
240             save_errno == ENETUNREACH ||
241             save_errno == ETIMEDOUT)  {
242             return -2   ;
243         }
244         return -1;
245     }
246
247     dbprintf(_("connected to %s\n"),
248               str_sockaddr(svaddr));
249     dbprintf(_("our side is %s\n"),
250               str_sockaddr(addrp));
251     return s;
252 }
253
254
255 /*
256  * Bind to a port in the given range.  Takes a begin,end pair of port numbers.
257  *
258  * Returns negative on error (EGAIN if all ports are in use).  Returns 0
259  * on success.
260  */
261 int
262 bind_portrange(
263     int                 s,
264     sockaddr_union *addrp,
265     in_port_t           first_port,
266     in_port_t           last_port,
267     char *              proto)
268 {
269     in_port_t port;
270     in_port_t cnt;
271     socklen_t_equiv socklen;
272     struct servent *servPort;
273     const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
274     int save_errno = EAGAIN;
275
276     assert(first_port <= last_port);
277
278     /*
279      * We pick a different starting port based on our pid and the current
280      * time to avoid always picking the same reserved port twice.
281      */
282     port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
283
284     /*
285      * Scan through the range, trying all available ports that are either 
286      * not taken in /etc/services or registered for *amanda*.  Wrap around
287      * if we don't happen to start at the beginning.
288      */
289     for (cnt = 0; cnt < num_ports; cnt++) {
290         servPort = getservbyport((int)htons(port), proto);
291         if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
292             SU_SET_PORT(addrp, port);
293             socklen = SS_LEN(addrp);
294             if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) {
295                 if (servPort == NULL) {
296                     g_debug(_("bind_portrange2: Try  port %d: Available - Success"), port);
297                 } else {
298                     g_debug(_("bind_portrange2: Try  port %d: Owned by %s - Success."), port, servPort->s_name);
299                 }
300                 return 0;
301             }
302             if (errno != EAGAIN && errno != EBUSY)
303                 save_errno = errno;
304             if (servPort == NULL) {
305                 g_debug(_("bind_portrange2: Try  port %d: Available - %s"),
306                         port, strerror(errno));
307             } else {
308                 g_debug(_("bind_portrange2: Try  port %d: Owned by %s - %s"),
309                         port, servPort->s_name, strerror(errno));
310             }
311         } else {
312                 g_debug(_("bind_portrange2: Skip port %d: Owned by %s."),
313                       port, servPort->s_name);
314         }
315         if (++port > last_port)
316             port = first_port;
317     }
318     g_debug(_("bind_portrange: all ports between %d and %d busy"),
319                   first_port,
320                   last_port);
321     errno = save_errno;
322     return -1;
323 }
324
325 int
326 interruptible_accept(
327     int sock,
328     struct sockaddr *addr,
329     socklen_t *addrlen,
330     gboolean (*prolong)(gpointer data),
331     gpointer prolong_data)
332 {
333     SELECT_ARG_TYPE readset;
334     struct timeval tv;
335     int nfound;
336
337     if (sock < 0 || sock >= FD_SETSIZE) {
338         g_debug("interruptible_accept: bad socket %d", sock);
339         return EBADF;
340     }
341
342     memset(&readset, 0, SIZEOF(readset));
343
344     while (1) {
345         if (!prolong(prolong_data)) {
346             errno = 0;
347             return -1;
348         }
349
350         FD_ZERO(&readset);
351         FD_SET(sock, &readset);
352
353         /* try accepting for 1s */
354         memset(&tv, 0, SIZEOF(tv));
355         tv.tv_sec = 1;
356
357         nfound = select(sock+1, &readset, NULL, NULL, &tv);
358         if (nfound < 0) {
359             return -1;
360         } else if (nfound == 0) {
361             continue;
362         } else if (!FD_ISSET(sock, &readset)) {
363             g_debug("interruptible_accept: select malfunction");
364             errno = EBADF;
365             return -1;
366         } else {
367             int rv = accept(sock, addr, addrlen);
368             if (rv < 0 && errno == EAGAIN)
369                 continue;
370             return rv;
371         }
372     }
373 }
374
375 /*
376  * Writes out the entire iovec
377  */
378 ssize_t
379 full_writev(
380     int                 fd,
381     struct iovec *      iov,
382     int                 iovcnt)
383 {
384     ssize_t delta, n, total;
385
386     assert(iov != NULL);
387
388     total = 0;
389     while (iovcnt > 0) {
390         /*
391          * Write the iovec
392          */
393         n = writev(fd, iov, iovcnt);
394         if (n < 0) {
395             if (errno != EINTR)
396                 return (-1);
397         }
398         else if (n == 0) {
399             errno = EIO;
400             return (-1);
401         } else {
402             total += n;
403             /*
404              * Iterate through each iov.  Figure out what we still need
405              * to write out.
406              */
407             for (; n > 0; iovcnt--, iov++) {
408                 /* 'delta' is the bytes written from this iovec */
409                 delta = ((size_t)n < (size_t)iov->iov_len) ? n : (ssize_t)iov->iov_len;
410                 /* subtract from the total num bytes written */
411                 n -= delta;
412                 assert(n >= 0);
413                 /* subtract from this iovec */
414                 iov->iov_len -= delta;
415                 iov->iov_base = (char *)iov->iov_base + delta;
416                 /* if this iovec isn't empty, run the writev again */
417                 if (iov->iov_len > 0)
418                     break;
419             }
420         }
421     }
422     return (total);
423 }
424
425
426 /*
427  * For backward compatibility we are trying for minimal quoting.  Unless ALWAYS
428  * is true, we only quote a string if it contains whitespace or is misquoted...
429  */
430
431 char *
432 quote_string_maybe(
433     const char *str,
434     gboolean always)
435 {
436     char *  s;
437     char *  ret;
438
439     if ((str == NULL) || (*str == '\0')) {
440         ret = stralloc("\"\"");
441     } else {
442         const char *r;
443         for (r = str; *r; r++) {
444             if (*r == ':' || *r == '\'' || *r == '\\' || *r == '\"' ||
445                 *r <= ' ' || *r == 0x7F )
446                 always = 1;
447         }
448         if (!always) {
449             /*
450              * String does not need to be quoted since it contains
451              * neither whitespace, control or quote characters.
452              */
453             ret = stralloc(str);
454         } else {
455             /*
456              * Allocate maximum possible string length.
457              * (a string of all quotes plus room for leading ", trailing " and
458              *  NULL)
459              */
460             ret = s = alloc((strlen(str) * 2) + 2 + 1);
461             *(s++) = '"';
462             while (*str != '\0') {
463                 if (*str == '\t') {
464                     *(s++) = '\\';
465                     *(s++) = 't';
466                     str++;
467                     continue;
468                 } else if (*str == '\n') {
469                     *(s++) = '\\';
470                     *(s++) = 'n';
471                     str++;
472                     continue;
473                 } else if (*str == '\r') {
474                     *(s++) = '\\';
475                     *(s++) = 'r';
476                     str++;
477                     continue;
478                 } else if (*str == '\f') {
479                     *(s++) = '\\';
480                     *(s++) = 'f';
481                     str++;
482                     continue;
483                 } else if (*str == '\\') {
484                     *(s++) = '\\';
485                     *(s++) = '\\';
486                     str++;
487                     continue;
488                 }
489                 if (*str == '"')
490                     *(s++) = '\\';
491                 *(s++) = *(str++);
492             }
493             *(s++) = '"';
494             *s = '\0';
495         }
496     }
497     return (ret);
498 }
499
500
501 int
502 len_quote_string_maybe(
503     const char *str,
504     gboolean always)
505 {
506     int   ret;
507
508     if ((str == NULL) || (*str == '\0')) {
509         ret = 0;
510     } else {
511         const char *r;
512         for (r = str; *r; r++) {
513             if (*r == ':' || *r == '\'' || *r == '\\' || *r == '\"' ||
514                 *r <= ' ' || *r == 0x7F )
515                 always = 1;
516         }
517         if (!always) {
518             /*
519              * String does not need to be quoted since it contains
520              * neither whitespace, control or quote characters.
521              */
522             ret = strlen(str);
523         } else {
524             /*
525              * Allocate maximum possible string length.
526              * (a string of all quotes plus room for leading ", trailing " and
527              *  NULL)
528              */
529             ret = 1;
530                 while (*str != '\0') {
531                 if (*str == '\t') {
532                     ret++;
533                     ret++;
534                     str++;
535                     continue;
536                 } else if (*str == '\n') {
537                     ret++;
538                     ret++;
539                     str++;
540                     continue;
541                 } else if (*str == '\r') {
542                     ret++;
543                     ret++;
544                     str++;
545                     continue;
546                 } else if (*str == '\f') {
547                     ret++;
548                     ret++;
549                     str++;
550                     continue;
551                 } else if (*str == '\\') {
552                     ret++;
553                     ret++;
554                     str++;
555                     continue;
556                 }
557                 if (*str == '"')
558                     ret++;
559                 ret++;
560                 str++;
561             }
562             ret++;
563         }
564     }
565     return (ret);
566 }
567
568
569 char *
570 unquote_string(
571     const char *str)
572 {
573     char * ret;
574
575     if ((str == NULL) || (*str == '\0')) {
576         ret = stralloc("");
577     } else {
578         char * in;
579         char * out;
580
581         ret = in = out = stralloc(str);
582         while (*in != '\0') {
583             if (*in == '"') {
584                 in++;
585                 continue;
586             }
587
588             if (*in == '\\') {
589                 in++;
590                 if (*in == 'n') {
591                     in++;
592                     *(out++) = '\n';
593                     continue;
594                 } else if (*in == 't') {
595                     in++;
596                     *(out++) = '\t';
597                     continue;
598                 } else if (*in == 'r') {
599                     in++;
600                     *(out++) = '\r';
601                     continue;
602                 } else if (*in == 'f') {
603                     in++;
604                     *(out++) = '\f';
605                     continue;
606                 } else if (*in >= '0' && *in <= '7') {
607                     char c = 0;
608                     int i = 0;
609
610                     while (i < 3 && *in >= '0' && *in <= '7') {
611                         c = (c << 3) + *(in++) - '0';
612                         i++;
613                     }
614                     if (c)
615                         *(out++) = c;
616                 } else if (*in == '\0') {
617                     /* trailing backslash -- ignore */
618                     break;
619                 }
620             }
621             *(out++) = *(in++);
622         }
623         *out = '\0';
624     }
625     return (ret);
626 }
627
628 gchar **
629 split_quoted_strings(
630     const gchar *string)
631 {
632     char *local;
633     char *start;
634     char *p;
635     char **result;
636     GPtrArray *strs;
637     int iq = 0;
638
639     if (!string)
640         return NULL;
641
642     p = start = local = g_strdup(string);
643     strs = g_ptr_array_new();
644
645     while (*p) {
646         if (!iq && *p == ' ') {
647             *p = '\0';
648             g_ptr_array_add(strs, unquote_string(start));
649             start = p+1;
650         } else if (*p == '\\') {
651             /* next character is taken literally; if it's a multicharacter
652              * escape (e.g., \171), that doesn't bother us here */
653             p++;
654             if (!*p) break;
655         } else if (*p == '\"') {
656             iq = ! iq;
657         }
658
659         p++;
660     }
661     if (start != string)
662         g_ptr_array_add(strs, unquote_string(start));
663
664     /* now convert strs into a strv, by stealing its references to the underlying
665      * strings */
666     result = g_new0(char *, strs->len + 1);
667     memmove(result, strs->pdata, sizeof(char *) * strs->len);
668
669     g_ptr_array_free(strs, TRUE); /* TRUE => free pdata, strings are not freed */
670     g_free(local);
671
672     return result;
673 }
674
675 char *
676 strquotedstr(char **saveptr)
677 {
678     char *  tok = strtok_r(NULL, " ", saveptr);
679     size_t      len;
680     int         in_quote;
681     int         in_backslash;
682     char       *p, *t;
683
684     if (!tok)
685         return tok;
686     len = strlen(tok);
687     in_quote = 0;
688     in_backslash = 0;
689     p = tok;
690     while (in_quote || in_backslash || *p != '\0') {
691         if (*p == '\0') {
692             /* append a new token */
693             t = strtok_r(NULL, " ", saveptr);
694             if (!t)
695                 return NULL;
696             tok[len] = ' ';
697             len = strlen(tok);
698         }
699         if (!in_backslash) {
700             if (*p == '"')
701                 in_quote = !in_quote;
702             else if (*p == '\\') {
703                 in_backslash = 1;
704             }
705         } else {
706            in_backslash = 0;
707         }
708         p++;
709     }
710     return tok;
711 }
712
713 char *
714 sanitize_string(
715     const char *str)
716 {
717     char * s;
718     char * ret;
719
720     if ((str == NULL) || (*str == '\0')) {
721         ret = stralloc("");
722     } else {
723         ret = stralloc(str);
724         for (s = ret; *s != '\0'; s++) {
725             if (iscntrl((int)*s))
726                 *s = '?';
727         }
728     }
729     return (ret);
730 }
731
732 char *hexencode_string(const char *str)
733 {
734     size_t orig_len, new_len, i;
735     GString *s;
736     gchar *ret;
737     if (!str) {
738         s = g_string_sized_new(0);
739         goto cleanup;
740     }
741     new_len = orig_len = strlen(str);
742     for (i = 0; i < orig_len; i++) {
743         if (!g_ascii_isalnum(str[i])) {
744             new_len += 2;
745         }
746     }
747     s = g_string_sized_new(new_len);
748
749     for (i = 0; i < orig_len; i++) {
750         if (g_ascii_isalnum(str[i])) {
751             g_string_append_c(s, str[i]);
752         } else {
753             g_string_append_printf(s, "%%%02hhx", str[i]);
754         }
755     }
756
757 cleanup:
758     ret = s->str;
759     g_string_free(s, FALSE);
760     return ret;
761 }
762
763 char *hexdecode_string(const char *str, GError **err)
764 {
765     size_t orig_len, new_len, i;
766     GString *s;
767     gchar *ret;
768     if (!str) {
769         s = g_string_sized_new(0);
770         goto cleanup;
771     }
772     new_len = orig_len = strlen(str);
773     for (i = 0; i < orig_len; i++) {
774         if (str[i] == '%') {
775             new_len -= 2;
776         }
777     }
778     s = g_string_sized_new(new_len);
779
780     for (i = 0; (orig_len > 2) && (i < orig_len-2); i++) {
781         if (str[i] == '%') {
782             gchar tmp = 0;
783             size_t j;
784             for (j = 1; j < 3; j++) {
785                 tmp <<= 4;
786                 if (str[i+j] >= '0' && str[i+j] <= '9') {
787                     tmp += str[i+j] - '0';
788                 } else if (str[i+j] >= 'a' && str[i+j] <= 'f') {
789                     tmp += str[i+j] - 'a' + 10;
790                 } else if (str[i+j] >= 'A' && str[i+j] <= 'F') {
791                     tmp += str[i+j] - 'A' + 10;
792                 } else {
793                     /* error */
794                     g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
795                         "Illegal character (non-hex) 0x%02hhx at offset %zd", str[i+j], i+j);
796                     g_string_truncate(s, 0);
797                     goto cleanup;
798                 }
799             }
800             if (!tmp) {
801                 g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
802                     "Encoded NULL at starting offset %zd", i);
803                 g_string_truncate(s, 0);
804                 goto cleanup;
805             }
806             g_string_append_c(s, tmp);
807             i += 2;
808         } else {
809             g_string_append_c(s, str[i]);
810         }
811     }
812     for ( /*nothing*/; i < orig_len; i++) {
813         if (str[i] == '%') {
814             g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
815                 "'%%' found at offset %zd, but fewer than two characters follow it (%zd)", i, orig_len-i-1);
816             g_string_truncate(s, 0);
817             goto cleanup;
818         } else {
819             g_string_append_c(s, str[i]);
820         }
821     }
822
823 cleanup:
824     ret = s->str;
825     g_string_free(s, FALSE);
826     return ret;
827 }
828
829 /* Helper for parse_braced_component; this will turn a single element array
830  * matching /^\d+\.\.\d+$/ into a sequence of numbered array elements. */
831 static GPtrArray *
832 expand_braced_sequence(GPtrArray *arr)
833 {
834     char *elt, *p;
835     char *l, *r;
836     int ldigits, rdigits, ndigits;
837     guint64 start, end;
838     gboolean leading_zero;
839
840     /* check whether the element matches the pattern */
841     /* expand last element of the array only */
842     elt = g_ptr_array_index(arr, arr->len-1);
843     ldigits = 0;
844     for (l = p = elt; *p && g_ascii_isdigit(*p); p++)
845         ldigits++;
846     if (ldigits == 0)
847         return arr;
848     if (*(p++) != '.')
849         return arr;
850     if (*(p++) != '.')
851         return arr;
852     rdigits = 0;
853     for (r = p; *p && g_ascii_isdigit(*p); p++)
854         rdigits++;
855     if (rdigits == 0)
856         return arr;
857     if (*p)
858         return arr;
859
860     /* we have a match, so extract start and end */
861     start = g_ascii_strtoull(l, NULL, 10);
862     end = g_ascii_strtoull(r, NULL, 10);
863     leading_zero = *l == '0';
864     ndigits = MAX(ldigits, rdigits);
865     if (start > end)
866         return arr;
867
868     /* sanity check.. */
869     if (end - start > 100000)
870         return arr;
871
872     /* remove last from the array */
873     g_ptr_array_remove_index(arr, arr->len - 1);
874
875     /* Add new elements */
876     while (start <= end) {
877         if (leading_zero) {
878             g_ptr_array_add(arr, g_strdup_printf("%0*ju",
879                         ndigits, (uintmax_t)start));
880         } else {
881             g_ptr_array_add(arr, g_strdup_printf("%ju", (uintmax_t)start));
882         }
883         start++;
884     }
885
886     return arr;
887 }
888
889 /* Helper for expand_braced_alternates; returns a list of un-escaped strings
890  * for the first "component" of str, where a component is a plain string or a
891  * brace-enclosed set of alternatives.  str is pointing to the first character
892  * of the next component on return. */
893 static GPtrArray *
894 parse_braced_component(char **str)
895 {
896     GPtrArray *result = g_ptr_array_new();
897
898     if (**str == '{') {
899         char *p = (*str)+1;
900         char *local = g_malloc(strlen(*str)+1);
901         char *current = local;
902         char *c = current;
903
904         while (1) {
905             if (*p == '\0' || *p == '{') {
906                 /* unterminated { .. } or extra '{' */
907                 amfree(local);
908                 g_ptr_array_free(result, TRUE);
909                 return NULL;
910             }
911
912             if (*p == '}' || *p == ',') {
913                 *c = '\0';
914                 g_ptr_array_add(result, g_strdup(current));
915                 result = expand_braced_sequence(result);
916                 current = ++c;
917
918                 if (*p == '}')
919                     break;
920                 else
921                     p++;
922             }
923
924             if (*p == '\\') {
925                 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
926                     p++;
927             }
928             *(c++) = *(p++);
929         }
930
931         amfree(local);
932
933         if (*p)
934             *str = p+1;
935         else
936             *str = p;
937     } else {
938         /* no braces -- just un-escape a plain string */
939         char *local = g_malloc(strlen(*str)+1);
940         char *r = local;
941         char *p = *str;
942
943         while (*p && *p != '{') {
944             if (*p == '\\') {
945                 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
946                     p++;
947             }
948             *(r++) = *(p++);
949         }
950         *r = '\0';
951         g_ptr_array_add(result, local);
952         *str = p;
953     }
954
955     return result;
956 }
957
958 GPtrArray *
959 expand_braced_alternates(
960     char * source)
961 {
962     GPtrArray *rval = g_ptr_array_new();
963
964     g_ptr_array_add(rval, g_strdup(""));
965
966     while (*source) {
967         GPtrArray *new_components;
968         GPtrArray *new_rval;
969         guint i, j;
970
971         new_components = parse_braced_component(&source);
972         if (!new_components) {
973             /* parse error */
974             g_ptr_array_free(rval, TRUE);
975             return NULL;
976         }
977
978         new_rval = g_ptr_array_new();
979
980         /* do a cartesian join of rval and new_components */
981         for (i = 0; i < rval->len; i++) {
982             for (j = 0; j < new_components->len; j++) {
983                 g_ptr_array_add(new_rval, g_strconcat(
984                     g_ptr_array_index(rval, i),
985                     g_ptr_array_index(new_components, j),
986                     NULL));
987             }
988         }
989
990         g_ptr_array_free(rval, TRUE);
991         g_ptr_array_free(new_components, TRUE);
992         rval = new_rval;
993     }
994
995     return rval;
996 }
997
998 char *
999 collapse_braced_alternates(
1000     GPtrArray *source)
1001 {
1002     GString *result = NULL;
1003     guint i;
1004
1005     result = g_string_new("{");
1006
1007     for (i = 0; i < source->len; i ++) {
1008         const char *str = g_ptr_array_index(source, i);
1009         char *qstr = NULL;
1010
1011         if (strchr(str, ',') || strchr(str, '\\') ||
1012             strchr(str, '{') || strchr(str, '}')) {
1013             const char *s;
1014             char *d;
1015
1016             s = str;
1017             qstr = d = g_malloc(strlen(str)*2+1);
1018             while (*s) {
1019                 if (*s == ',' || *s == '\\' || *s == '{' || *s == '}')
1020                     *(d++) = '\\';
1021                 *(d++) = *(s++);
1022             }
1023             *(d++) = '\0';
1024         }
1025         g_string_append_printf(result, "%s%s", qstr? qstr : str,
1026                 (i < source->len-1)? "," : "");
1027         if (qstr)
1028             g_free(qstr);
1029     }
1030
1031     g_string_append(result, "}");
1032     return g_string_free(result, FALSE);
1033 }
1034
1035 /*
1036    Return 0 if the following characters are present
1037    * ( ) < > [ ] , ; : ! $ \ / "
1038    else returns 1
1039 */
1040
1041 int
1042 validate_mailto(
1043     const char *mailto)
1044 {
1045     return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
1046 }
1047
1048 int copy_file(
1049     char  *dst,
1050     char  *src,
1051     char **errmsg)
1052 {
1053     int     infd, outfd;
1054     int     save_errno;
1055     size_t nb;
1056     char    buf[32768];
1057     char   *quoted;
1058
1059     if ((infd = open(src, O_RDONLY)) == -1) {
1060         save_errno = errno;
1061         quoted = quote_string(src);
1062         *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
1063                             quoted, strerror(save_errno));
1064         amfree(quoted);
1065         return -1;
1066     }
1067
1068     if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
1069         save_errno = errno;
1070         quoted = quote_string(dst);
1071         *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
1072                             quoted, strerror(save_errno));
1073         amfree(quoted);
1074         close(infd);
1075         return -1;
1076     }
1077
1078     while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
1079         if(full_write(outfd,&buf,nb) < nb) {
1080             save_errno = errno;
1081             quoted = quote_string(dst);
1082             *errmsg = vstrallocf(_("Error writing to '%s': %s"),
1083                                 quoted, strerror(save_errno));
1084             amfree(quoted);
1085             close(infd);
1086             close(outfd);
1087             return -1;
1088         }
1089     }
1090
1091     if (errno != 0) {
1092         save_errno = errno;
1093         quoted = quote_string(src);
1094         *errmsg = vstrallocf(_("Error reading from '%s': %s"),
1095                             quoted, strerror(save_errno));
1096         amfree(quoted);
1097         close(infd);
1098         close(outfd);
1099         return -1;
1100     }
1101
1102     close(infd);
1103     close(outfd);
1104     return 0;
1105 }
1106
1107 #ifndef HAVE_LIBREADLINE
1108 /*
1109  * simple readline() replacements, used when we don't have readline
1110  * support from the system.
1111  */
1112
1113 char *
1114 readline(
1115     const char *prompt)
1116 {
1117     g_printf("%s", prompt);
1118     fflush(stdout);
1119     fflush(stderr);
1120     return agets(stdin);
1121 }
1122
1123 void 
1124 add_history(
1125     const char *line)
1126 {
1127     (void)line;         /* Quiet unused parameter warning */
1128 }
1129
1130 #endif
1131
1132 /* Order of preference: readdir64(), readdir(). */
1133 #if HAVE_DECL_READDIR64
1134 #  define USE_DIRENT64
1135 #  define USE_READDIR64
1136 #elif HAVE_DECL_READDIR
1137 #  define USE_READDIR
1138 #else
1139 # error No readdir() or readdir64() available!
1140 #endif
1141
1142 char * portable_readdir(DIR* handle) {
1143
1144 #ifdef USE_DIRENT64
1145     struct dirent64 *entry_p;
1146 #else
1147     struct dirent *entry_p;
1148 #endif
1149
1150     static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1151
1152     g_static_mutex_lock(&mutex);
1153
1154 #ifdef USE_READDIR
1155     entry_p = readdir(handle);
1156 #endif
1157 #ifdef USE_READDIR64
1158     entry_p = readdir64(handle);
1159 #endif
1160
1161     g_static_mutex_unlock(&mutex);
1162     
1163     if (entry_p == NULL)
1164         return NULL;
1165
1166     /* FIXME: According to glibc documentation, d_name may not be
1167        null-terminated in some cases on some very old platforms. Not
1168        sure what to do about that case. */
1169     return strdup(entry_p->d_name);
1170 }
1171
1172 int search_directory(DIR * handle, const char * regex,
1173                      SearchDirectoryFunctor functor, gpointer user_data) {
1174     int rval = 0;
1175     regex_t compiled_regex;
1176     gboolean done = FALSE;
1177
1178     if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
1179         regfree(&compiled_regex);
1180         return -1;
1181     }
1182
1183     rewinddir(handle);
1184
1185     while (!done) {
1186         char * read_name;
1187         int result;
1188         read_name = portable_readdir(handle);
1189         if (read_name == NULL) {
1190             regfree(&compiled_regex);
1191             return rval;
1192         }
1193         result = regexec(&compiled_regex, read_name, 0, NULL, 0);
1194         if (result == 0) {
1195             rval ++;
1196             done = !functor(read_name, user_data);
1197         }
1198         amfree(read_name);
1199     }
1200     regfree(&compiled_regex);
1201     return rval;
1202 }
1203
1204 char* find_regex_substring(const char* base_string, const regmatch_t match) {
1205     char * rval;
1206     int size;
1207
1208     size = match.rm_eo - match.rm_so;
1209     rval = malloc(size+1);
1210     memcpy(rval, base_string + match.rm_so, size);
1211     rval[size] = '\0';
1212
1213     return rval;
1214 }
1215
1216 int compare_possibly_null_strings(const char * a, const char * b) {
1217     if (a == b) {
1218         /* NULL or otherwise, they're the same. */
1219         return 0;
1220     } else if (a == NULL) {
1221         /* b != NULL */
1222         return -1;
1223     } else if (b == NULL) {
1224         /* a != NULL */
1225         return 1;
1226     } else {
1227         /* a != NULL != b */
1228         return strcmp(a, b);
1229     }
1230 }
1231
1232 int
1233 resolve_hostname(const char *hostname,
1234         int socktype,
1235         struct addrinfo **res,
1236         char **canonname)
1237 {
1238     struct addrinfo hints;
1239     struct addrinfo *myres;
1240     int flags = 0;
1241     int result;
1242
1243     if (res) *res = NULL;
1244     if (canonname) {
1245         *canonname = NULL;
1246         flags = AI_CANONNAME;
1247     }
1248
1249 #ifdef AI_ADDRCONFIG
1250     flags |= AI_ADDRCONFIG;
1251 #endif
1252
1253     memset(&hints, 0, sizeof(hints));
1254 #ifdef WORKING_IPV6
1255     /* get any kind of addresss */
1256     hints.ai_family = AF_UNSPEC;
1257 #else
1258     /* even if getaddrinfo supports IPv6, don't let it return
1259      * such an address */
1260     hints.ai_family = AF_INET;
1261 #endif
1262     hints.ai_flags = flags;
1263     hints.ai_socktype = socktype;
1264     result = getaddrinfo(hostname, NULL, &hints, &myres);
1265     if (result != 0) {
1266         return result;
1267     }
1268
1269     if (canonname && myres && myres->ai_canonname) {
1270         *canonname = stralloc(myres->ai_canonname);
1271     }
1272
1273     if (res) {
1274         *res = myres;
1275     } else {
1276         freeaddrinfo(myres);
1277     }
1278
1279     return result;
1280 }
1281
1282 char *
1283 _str_exit_status(
1284     char *subject,
1285     amwait_t status)
1286 {
1287     if (WIFEXITED(status)) {
1288         int exitstatus = WEXITSTATUS(status);
1289         if (exitstatus == 0)
1290             return vstrallocf(_("%s exited normally"), subject);
1291         else
1292             return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
1293     }
1294
1295     if (WIFSIGNALED(status)) {
1296         int signal = WTERMSIG(status);
1297 #ifdef WCOREDUMP
1298         if (WCOREDUMP(status))
1299             return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
1300                 subject, signal);
1301         else
1302 #endif
1303             return vstrallocf(_("%s exited after receiving signal %d"),
1304                 subject, signal);
1305     }
1306
1307     if (WIFSTOPPED(status)) {
1308         int signal = WSTOPSIG(status);
1309         return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
1310             subject, signal);
1311     }
1312
1313 #ifdef WIFCONTINUED
1314     if (WIFCONTINUED(status)) {
1315         return vstrallocf(_("%s was resumed"), subject);
1316     }
1317 #endif
1318
1319     return vstrallocf(_("%s exited in unknown circumstances"), subject);
1320 }
1321
1322 void
1323 check_running_as(running_as_flags who)
1324 {
1325 #ifdef CHECK_USERID
1326     struct passwd *pw;
1327     uid_t uid_me;
1328     uid_t uid_target;
1329     char *uname_me = NULL;
1330     char *uname_target = NULL;
1331     char *dumpuser;
1332
1333     uid_me = getuid();
1334     if ((pw = getpwuid(uid_me)) == NULL) {
1335         error(_("current userid %ld not found in password database"), (long)uid_me);
1336         /* NOTREACHED */
1337     }
1338     uname_me = stralloc(pw->pw_name);
1339
1340 #ifndef SINGLE_USERID
1341     if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
1342         error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
1343                 (long long int)geteuid(), (long long int)uid_me);
1344         /* NOTREACHED */
1345     }
1346 #endif
1347
1348     switch (who & RUNNING_AS_USER_MASK) {
1349         case RUNNING_AS_ANY:
1350             uid_target = uid_me;
1351             uname_target = uname_me;
1352             amfree(uname_me);
1353             return;
1354
1355         case RUNNING_AS_ROOT:
1356             uid_target = 0;
1357             uname_target = "root";
1358             break;
1359
1360         case RUNNING_AS_DUMPUSER_PREFERRED:
1361             dumpuser = getconf_str(CNF_DUMPUSER);
1362             if ((pw = getpwnam(dumpuser)) != NULL &&
1363                     uid_me != pw->pw_uid) {
1364                 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
1365                     uid_me == pw->pw_uid) {
1366                     /* uid == CLIENT_LOGIN: not ideal, but OK */
1367                     dbprintf(_("NOTE: running as '%s', which is the client"
1368                                " user, not the dumpuser ('%s'); forging"
1369                                " on anyway\n"),
1370                              CLIENT_LOGIN, dumpuser);
1371                     uid_target = uid_me; /* force success below */
1372                    break;
1373                 }
1374             }
1375             /* FALLTHROUGH */
1376
1377         case RUNNING_AS_DUMPUSER:
1378             uname_target = getconf_str(CNF_DUMPUSER);
1379             if ((pw = getpwnam(uname_target)) == NULL) {
1380                 error(_("cannot look up dumpuser \"%s\""), uname_target);
1381                 /*NOTREACHED*/
1382             }
1383             uid_target = pw->pw_uid;
1384             break;
1385
1386         case RUNNING_AS_CLIENT_LOGIN:
1387             uname_target = CLIENT_LOGIN;
1388             if ((pw = getpwnam(uname_target)) == NULL) {
1389                 error(_("cannot look up client user \"%s\""), uname_target);
1390                 /*NOTREACHED*/
1391             }
1392             uid_target = pw->pw_uid;
1393             break;
1394
1395         default:
1396             error(_("Unknown check_running_as() call"));
1397             /* NOTREACHED */
1398     }
1399
1400     if (uid_me != uid_target) {
1401         error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
1402         /*NOTREACHED*/
1403     }
1404     amfree(uname_me);
1405
1406 #else
1407     /* Quiet unused variable warning */
1408     (void)who;
1409 #endif
1410 }
1411
1412 int
1413 set_root_privs(int need_root)
1414 {
1415 #ifndef SINGLE_USERID
1416     static gboolean first_call = TRUE;
1417     static uid_t unpriv = 1;
1418
1419     if (first_call) {
1420         /* save the original real userid (that of our invoker) */
1421         unpriv = getuid();
1422
1423         /* and set all of our userids (including, importantly, the saved
1424          * userid) to 0 */
1425         setuid(0);
1426
1427         /* don't need to do this next time */
1428         first_call = FALSE;
1429     }
1430
1431     if (need_root == 1) {
1432         if (geteuid() == 0) return 1; /* already done */
1433
1434         if (seteuid(0) == -1) return 0;
1435         /* (we don't switch the group back) */
1436     } else if (need_root == -1) {
1437         /* make sure the euid is 0 so that we can set the uid */
1438         if (geteuid() != 0) {
1439             if (seteuid(0) == -1) return 0;
1440         }
1441
1442         /* now set the uid to the unprivileged userid */
1443         if (setuid(unpriv) == -1) return 0;
1444     } else {
1445         if (geteuid() != 0) return 1; /* already done */
1446
1447         /* set the *effective* userid only */
1448         if (seteuid(unpriv) == -1) return 0;
1449         if (setegid(getgid()) == -1) return 0;
1450     }
1451 #else
1452     (void)need_root; /* Quiet unused variable warning */
1453 #endif
1454     return 1;
1455 }
1456
1457 int
1458 become_root(void)
1459 {
1460 #ifndef SINGLE_USERID
1461     /* first, set the effective userid to 0 */
1462     if (seteuid(0) == -1) return 0;
1463
1464     /* then, set all of the userids to 0 */
1465     if (setuid(0) == -1) return 0;
1466 #endif
1467     return 1;
1468 }
1469
1470 char *
1471 base64_decode_alloc_string(
1472     char *in)
1473 {
1474     char   *out;
1475     size_t  in_len = strlen(in);
1476     size_t  out_len = 3 * (in_len / 4) + 3;
1477
1478     out = malloc(out_len);
1479     if (!base64_decode(in, in_len, out, &out_len)) {
1480         amfree(out);
1481         return NULL;
1482     }
1483     out[out_len] = '\0';
1484
1485     return out;
1486 }
1487
1488
1489 /* A GHFunc (callback for g_hash_table_foreach),
1490  * Store a property and it's value in an ARGV.
1491  *
1492  * @param key_p: (char *) property name.
1493  * @param value_p: (GSList *) property values list.
1494  * @param user_data_p: (char ***) pointer to ARGV.
1495  */
1496 static void
1497 proplist_add_to_argv(
1498     gpointer key_p,
1499     gpointer value_p,
1500     gpointer user_data_p)
1501 {
1502     char         *property_s = key_p;
1503     property_t   *value_s = value_p;
1504     GPtrArray    *argv_ptr = user_data_p;
1505     GSList       *value;
1506     char         *q, *w, *qprop;
1507
1508     q = stralloc(property_s);
1509     /* convert to lower case */
1510     for (w=q; *w != '\0'; w++) {
1511         *w = tolower(*w);
1512         if (*w == '_')
1513             *w = '-';
1514     }
1515     qprop = stralloc2("--", q);
1516     amfree(q);
1517     for(value=value_s->values; value != NULL; value = value->next) {
1518         g_ptr_array_add(argv_ptr, stralloc(qprop));
1519         g_ptr_array_add(argv_ptr, stralloc((char *)value->data));
1520     }
1521     amfree(qprop);
1522 }
1523
1524 void
1525 property_add_to_argv(
1526     GPtrArray  *argv_ptr,
1527     GHashTable *proplist)
1528 {
1529     g_hash_table_foreach(proplist, &proplist_add_to_argv, argv_ptr);
1530 }
1531
1532
1533 /*
1534  * Process parameters
1535  */
1536
1537 static char *pname = NULL;
1538 static char *ptype = NULL;
1539 static pcontext_t pcontext = CONTEXT_DEFAULT; 
1540
1541 void
1542 set_pname(char *p)
1543 {
1544     pname = newstralloc(pname, p);
1545 }
1546
1547 char *
1548 get_pname(void)
1549 {
1550     if (!pname) pname = stralloc("unknown");
1551     return pname;
1552 }
1553
1554 void
1555 set_ptype(char *p)
1556 {
1557     ptype = newstralloc(ptype, p);
1558 }
1559
1560 char *
1561 get_ptype(void)
1562 {
1563     if (!ptype) ptype = stralloc("unknown");
1564     return ptype;
1565 }
1566
1567 void
1568 set_pcontext(pcontext_t pc)
1569 {
1570     pcontext = pc;
1571 }
1572
1573 pcontext_t
1574 get_pcontext(void)
1575 {
1576     return pcontext;
1577 }
1578
1579 #ifdef __OpenBSD__
1580 void
1581 openbsd_fd_inform(void)
1582 {
1583     int i;
1584     for (i = DATA_FD_OFFSET; i < DATA_FD_OFFSET + DATA_FD_COUNT*2; i++) {
1585         /* a simple fcntl() will cause the library to "look" at this file
1586          * descriptor, which is good enough */
1587         (void)fcntl(i, F_GETFL);
1588     }
1589 }
1590 #endif
1591
1592 void
1593 debug_executing(
1594     GPtrArray *argv_ptr)
1595 {
1596     guint i;
1597     char *cmdline = stralloc((char *)g_ptr_array_index(argv_ptr, 0));
1598
1599     for (i = 1; i < argv_ptr->len-1; i++) {
1600         char *arg = g_shell_quote((char *)g_ptr_array_index(argv_ptr, i));
1601         cmdline = vstrextend(&cmdline, " ", arg, NULL);
1602         amfree(arg);
1603     }
1604     g_debug("Executing: %s\n", cmdline);
1605     amfree(cmdline);
1606 }
1607
1608 char *
1609 get_first_line(
1610     GPtrArray *argv_ptr)
1611 {
1612     char *output_string = NULL;
1613     int   inpipe[2], outpipe[2], errpipe[2];
1614     int   pid;
1615     FILE *out, *err;
1616
1617     assert(argv_ptr != NULL);
1618     assert(argv_ptr->pdata != NULL);
1619     assert(argv_ptr->len >= 1);
1620
1621     if (pipe(inpipe) == -1) {
1622         error(_("error [open pipe: %s]"), strerror(errno));
1623         /*NOTREACHED*/
1624     }
1625     if (pipe(outpipe) == -1) {
1626         error(_("error [open pipe: %s]"), strerror(errno));
1627         /*NOTREACHED*/
1628     }
1629     if (pipe(errpipe) == -1) {
1630         error(_("error [open pipe: %s]"), strerror(errno));
1631         /*NOTREACHED*/
1632     }
1633
1634     fflush(stdout);
1635     switch(pid = fork()) {
1636     case -1:
1637         error(_("error [fork: %s]"), strerror(errno));
1638         /*NOTREACHED*/
1639
1640     default:    /* parent process */
1641         aclose(inpipe[0]);
1642         aclose(outpipe[1]);
1643         aclose(errpipe[1]);
1644         break;
1645
1646     case 0: /* child process */
1647         aclose(inpipe[1]);
1648         aclose(outpipe[0]);
1649         aclose(errpipe[0]);
1650
1651         dup2(inpipe[0], 0);
1652         dup2(outpipe[1], 1);
1653         dup2(errpipe[1], 2);
1654
1655         debug_executing(argv_ptr);
1656         g_fprintf(stdout, "unknown\n");
1657         execv((char *)*argv_ptr->pdata, (char **)argv_ptr->pdata);
1658         error(_("error [exec %s: %s]"), (char *)*argv_ptr->pdata, strerror(errno));
1659     }
1660
1661     aclose(inpipe[1]);
1662
1663     out = fdopen(outpipe[0],"r");
1664     err = fdopen(errpipe[0],"r");
1665
1666     output_string = agets(out);
1667     if (!output_string)
1668         output_string = agets(err);
1669
1670     fclose(out);
1671     fclose(err);
1672
1673     waitpid(pid, NULL, 0);
1674
1675     return output_string;
1676 }
1677