Imported Upstream version 3.3.2
[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     gpointer *pdata;
964
965     g_ptr_array_add(rval, g_strdup(""));
966
967     while (*source) {
968         GPtrArray *new_components;
969         GPtrArray *new_rval;
970         guint i, j;
971
972         new_components = parse_braced_component(&source);
973         if (!new_components) {
974             /* parse error */
975             for (i = 0, pdata = rval->pdata; i < rval->len; i++)
976                 g_free(*pdata++);
977             g_ptr_array_free(rval, TRUE);
978             return NULL;
979         }
980
981         new_rval = g_ptr_array_new();
982
983         /* do a cartesian join of rval and new_components */
984         for (i = 0; i < rval->len; i++) {
985             for (j = 0; j < new_components->len; j++) {
986                 g_ptr_array_add(new_rval, g_strconcat(
987                     g_ptr_array_index(rval, i),
988                     g_ptr_array_index(new_components, j),
989                     NULL));
990             }
991         }
992
993         for (i = 0, pdata = rval->pdata; i < rval->len; i++)
994             g_free(*pdata++);
995         g_ptr_array_free(rval, TRUE);
996         for (i = 0, pdata = new_components->pdata; i < new_components->len; i++)
997             g_free(*pdata++);
998         g_ptr_array_free(new_components, TRUE);
999         rval = new_rval;
1000     }
1001
1002     return rval;
1003 }
1004
1005 char *
1006 collapse_braced_alternates(
1007     GPtrArray *source)
1008 {
1009     GString *result = NULL;
1010     guint i;
1011
1012     result = g_string_new("{");
1013
1014     for (i = 0; i < source->len; i ++) {
1015         const char *str = g_ptr_array_index(source, i);
1016         char *qstr = NULL;
1017
1018         if (strchr(str, ',') || strchr(str, '\\') ||
1019             strchr(str, '{') || strchr(str, '}')) {
1020             const char *s;
1021             char *d;
1022
1023             s = str;
1024             qstr = d = g_malloc(strlen(str)*2+1);
1025             while (*s) {
1026                 if (*s == ',' || *s == '\\' || *s == '{' || *s == '}')
1027                     *(d++) = '\\';
1028                 *(d++) = *(s++);
1029             }
1030             *(d++) = '\0';
1031         }
1032         g_string_append_printf(result, "%s%s", qstr? qstr : str,
1033                 (i < source->len-1)? "," : "");
1034         if (qstr)
1035             g_free(qstr);
1036     }
1037
1038     g_string_append(result, "}");
1039     return g_string_free(result, FALSE);
1040 }
1041
1042 /*
1043    Return 0 if the following characters are present
1044    * ( ) < > [ ] , ; : ! $ \ / "
1045    else returns 1
1046 */
1047
1048 int
1049 validate_mailto(
1050     const char *mailto)
1051 {
1052     return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
1053 }
1054
1055 int copy_file(
1056     char  *dst,
1057     char  *src,
1058     char **errmsg)
1059 {
1060     int     infd, outfd;
1061     int     save_errno;
1062     size_t nb;
1063     char    buf[32768];
1064     char   *quoted;
1065
1066     if ((infd = open(src, O_RDONLY)) == -1) {
1067         save_errno = errno;
1068         quoted = quote_string(src);
1069         *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
1070                             quoted, strerror(save_errno));
1071         amfree(quoted);
1072         return -1;
1073     }
1074
1075     if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
1076         save_errno = errno;
1077         quoted = quote_string(dst);
1078         *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
1079                             quoted, strerror(save_errno));
1080         amfree(quoted);
1081         close(infd);
1082         return -1;
1083     }
1084
1085     while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
1086         if(full_write(outfd,&buf,nb) < nb) {
1087             save_errno = errno;
1088             quoted = quote_string(dst);
1089             *errmsg = vstrallocf(_("Error writing to '%s': %s"),
1090                                 quoted, strerror(save_errno));
1091             amfree(quoted);
1092             close(infd);
1093             close(outfd);
1094             return -1;
1095         }
1096     }
1097
1098     if (errno != 0) {
1099         save_errno = errno;
1100         quoted = quote_string(src);
1101         *errmsg = vstrallocf(_("Error reading from '%s': %s"),
1102                             quoted, strerror(save_errno));
1103         amfree(quoted);
1104         close(infd);
1105         close(outfd);
1106         return -1;
1107     }
1108
1109     close(infd);
1110     close(outfd);
1111     return 0;
1112 }
1113
1114 #ifndef HAVE_LIBREADLINE
1115 /*
1116  * simple readline() replacements, used when we don't have readline
1117  * support from the system.
1118  */
1119
1120 char *
1121 readline(
1122     const char *prompt)
1123 {
1124     g_printf("%s", prompt);
1125     fflush(stdout);
1126     fflush(stderr);
1127     return agets(stdin);
1128 }
1129
1130 void 
1131 add_history(
1132     const char *line)
1133 {
1134     (void)line;         /* Quiet unused parameter warning */
1135 }
1136
1137 #endif
1138
1139 /* Order of preference: readdir64(), readdir(). */
1140 #if HAVE_DECL_READDIR64
1141 #  define USE_DIRENT64
1142 #  define USE_READDIR64
1143 #elif HAVE_DECL_READDIR
1144 #  define USE_READDIR
1145 #else
1146 # error No readdir() or readdir64() available!
1147 #endif
1148
1149 #if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
1150 # pragma GCC diagnostic push
1151 # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
1152 #endif
1153
1154 char * portable_readdir(DIR* handle) {
1155
1156 #ifdef USE_DIRENT64
1157     struct dirent64 *entry_p;
1158 #else
1159     struct dirent *entry_p;
1160 #endif
1161
1162     static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1163
1164     g_static_mutex_lock(&mutex);
1165
1166 #ifdef USE_READDIR
1167     entry_p = readdir(handle);
1168 #endif
1169 #ifdef USE_READDIR64
1170     entry_p = readdir64(handle);
1171 #endif
1172
1173     g_static_mutex_unlock(&mutex);
1174     
1175     if (entry_p == NULL)
1176         return NULL;
1177
1178     /* FIXME: According to glibc documentation, d_name may not be
1179        null-terminated in some cases on some very old platforms. Not
1180        sure what to do about that case. */
1181     return strdup(entry_p->d_name);
1182 }
1183 #if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
1184 # pragma GCC diagnostic pop
1185 #endif
1186
1187 int search_directory(DIR * handle, const char * regex,
1188                      SearchDirectoryFunctor functor, gpointer user_data) {
1189     int rval = 0;
1190     regex_t compiled_regex;
1191     gboolean done = FALSE;
1192
1193     if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
1194         regfree(&compiled_regex);
1195         return -1;
1196     }
1197
1198     rewinddir(handle);
1199
1200     while (!done) {
1201         char * read_name;
1202         int result;
1203         read_name = portable_readdir(handle);
1204         if (read_name == NULL) {
1205             regfree(&compiled_regex);
1206             return rval;
1207         }
1208         result = regexec(&compiled_regex, read_name, 0, NULL, 0);
1209         if (result == 0) {
1210             rval ++;
1211             done = !functor(read_name, user_data);
1212         }
1213         amfree(read_name);
1214     }
1215     regfree(&compiled_regex);
1216     return rval;
1217 }
1218
1219 char* find_regex_substring(const char* base_string, const regmatch_t match) {
1220     char * rval;
1221     int size;
1222
1223     size = match.rm_eo - match.rm_so;
1224     rval = malloc(size+1);
1225     memcpy(rval, base_string + match.rm_so, size);
1226     rval[size] = '\0';
1227
1228     return rval;
1229 }
1230
1231 int compare_possibly_null_strings(const char * a, const char * b) {
1232     if (a == b) {
1233         /* NULL or otherwise, they're the same. */
1234         return 0;
1235     } else if (a == NULL) {
1236         /* b != NULL */
1237         return -1;
1238     } else if (b == NULL) {
1239         /* a != NULL */
1240         return 1;
1241     } else {
1242         /* a != NULL != b */
1243         return strcmp(a, b);
1244     }
1245 }
1246
1247 int
1248 resolve_hostname(const char *hostname,
1249         int socktype,
1250         struct addrinfo **res,
1251         char **canonname)
1252 {
1253     struct addrinfo hints;
1254     struct addrinfo *myres;
1255     int flags = 0;
1256     int result;
1257
1258     if (res) *res = NULL;
1259     if (canonname) {
1260         *canonname = NULL;
1261         flags = AI_CANONNAME;
1262     }
1263
1264 #ifdef AI_ADDRCONFIG
1265     flags |= AI_ADDRCONFIG;
1266 #endif
1267
1268     memset(&hints, 0, sizeof(hints));
1269 #ifdef WORKING_IPV6
1270     /* get any kind of addresss */
1271     hints.ai_family = AF_UNSPEC;
1272 #else
1273     /* even if getaddrinfo supports IPv6, don't let it return
1274      * such an address */
1275     hints.ai_family = AF_INET;
1276 #endif
1277     hints.ai_flags = flags;
1278     hints.ai_socktype = socktype;
1279     result = getaddrinfo(hostname, NULL, &hints, &myres);
1280     if (result != 0) {
1281         return result;
1282     }
1283
1284     if (canonname && myres && myres->ai_canonname) {
1285         *canonname = stralloc(myres->ai_canonname);
1286     }
1287
1288     if (res) {
1289         *res = myres;
1290     } else {
1291         freeaddrinfo(myres);
1292     }
1293
1294     return result;
1295 }
1296
1297 char *
1298 _str_exit_status(
1299     char *subject,
1300     amwait_t status)
1301 {
1302     if (WIFEXITED(status)) {
1303         int exitstatus = WEXITSTATUS(status);
1304         if (exitstatus == 0)
1305             return vstrallocf(_("%s exited normally"), subject);
1306         else
1307             return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
1308     }
1309
1310     if (WIFSIGNALED(status)) {
1311         int signal = WTERMSIG(status);
1312 #ifdef WCOREDUMP
1313         if (WCOREDUMP(status))
1314             return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
1315                 subject, signal);
1316         else
1317 #endif
1318             return vstrallocf(_("%s exited after receiving signal %d"),
1319                 subject, signal);
1320     }
1321
1322     if (WIFSTOPPED(status)) {
1323         int signal = WSTOPSIG(status);
1324         return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
1325             subject, signal);
1326     }
1327
1328 #ifdef WIFCONTINUED
1329     if (WIFCONTINUED(status)) {
1330         return vstrallocf(_("%s was resumed"), subject);
1331     }
1332 #endif
1333
1334     return vstrallocf(_("%s exited in unknown circumstances"), subject);
1335 }
1336
1337 void
1338 check_running_as(running_as_flags who)
1339 {
1340 #ifdef CHECK_USERID
1341     struct passwd *pw;
1342     uid_t uid_me;
1343     uid_t uid_target;
1344     char *uname_me = NULL;
1345     char *uname_target = NULL;
1346     char *dumpuser;
1347
1348     uid_me = getuid();
1349     if ((pw = getpwuid(uid_me)) == NULL) {
1350         error(_("current userid %ld not found in password database"), (long)uid_me);
1351         /* NOTREACHED */
1352     }
1353     uname_me = stralloc(pw->pw_name);
1354
1355 #ifndef SINGLE_USERID
1356     if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
1357         error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
1358                 (long long int)geteuid(), (long long int)uid_me);
1359         /* NOTREACHED */
1360     }
1361 #endif
1362
1363     switch (who & RUNNING_AS_USER_MASK) {
1364         case RUNNING_AS_ANY:
1365             uid_target = uid_me;
1366             uname_target = uname_me;
1367             amfree(uname_me);
1368             return;
1369
1370         case RUNNING_AS_ROOT:
1371             uid_target = 0;
1372             uname_target = "root";
1373             break;
1374
1375         case RUNNING_AS_DUMPUSER_PREFERRED:
1376             dumpuser = getconf_str(CNF_DUMPUSER);
1377             if ((pw = getpwnam(dumpuser)) != NULL &&
1378                     uid_me != pw->pw_uid) {
1379                 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
1380                     uid_me == pw->pw_uid) {
1381                     /* uid == CLIENT_LOGIN: not ideal, but OK */
1382                     dbprintf(_("NOTE: running as '%s', which is the client"
1383                                " user, not the dumpuser ('%s'); forging"
1384                                " on anyway\n"),
1385                              CLIENT_LOGIN, dumpuser);
1386                     uid_target = uid_me; /* force success below */
1387                    break;
1388                 }
1389             }
1390             /* FALLTHROUGH */
1391
1392         case RUNNING_AS_DUMPUSER:
1393             uname_target = getconf_str(CNF_DUMPUSER);
1394             if ((pw = getpwnam(uname_target)) == NULL) {
1395                 error(_("cannot look up dumpuser \"%s\""), uname_target);
1396                 /*NOTREACHED*/
1397             }
1398             uid_target = pw->pw_uid;
1399             break;
1400
1401         case RUNNING_AS_CLIENT_LOGIN:
1402             uname_target = CLIENT_LOGIN;
1403             if ((pw = getpwnam(uname_target)) == NULL) {
1404                 error(_("cannot look up client user \"%s\""), uname_target);
1405                 /*NOTREACHED*/
1406             }
1407             uid_target = pw->pw_uid;
1408             break;
1409
1410         default:
1411             error(_("Unknown check_running_as() call"));
1412             /* NOTREACHED */
1413     }
1414
1415     if (uid_me != uid_target) {
1416         error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
1417         /*NOTREACHED*/
1418     }
1419     amfree(uname_me);
1420
1421 #else
1422     /* Quiet unused variable warning */
1423     (void)who;
1424 #endif
1425 }
1426
1427 int
1428 set_root_privs(int need_root)
1429 {
1430 #ifndef SINGLE_USERID
1431     static gboolean first_call = TRUE;
1432     static uid_t unpriv = 1;
1433
1434     if (first_call) {
1435         /* save the original real userid (that of our invoker) */
1436         unpriv = getuid();
1437
1438         /* and set all of our userids (including, importantly, the saved
1439          * userid) to 0 */
1440         setuid(0);
1441
1442         /* don't need to do this next time */
1443         first_call = FALSE;
1444     }
1445
1446     if (need_root == 1) {
1447         if (geteuid() == 0) return 1; /* already done */
1448
1449         if (seteuid(0) == -1) return 0;
1450         /* (we don't switch the group back) */
1451     } else if (need_root == -1) {
1452         /* make sure the euid is 0 so that we can set the uid */
1453         if (geteuid() != 0) {
1454             if (seteuid(0) == -1) return 0;
1455         }
1456
1457         /* now set the uid to the unprivileged userid */
1458         if (setuid(unpriv) == -1) return 0;
1459     } else {
1460         if (geteuid() != 0) return 1; /* already done */
1461
1462         /* set the *effective* userid only */
1463         if (seteuid(unpriv) == -1) return 0;
1464         if (setegid(getgid()) == -1) return 0;
1465     }
1466 #else
1467     (void)need_root; /* Quiet unused variable warning */
1468 #endif
1469     return 1;
1470 }
1471
1472 int
1473 become_root(void)
1474 {
1475 #ifndef SINGLE_USERID
1476     /* first, set the effective userid to 0 */
1477     if (seteuid(0) == -1) return 0;
1478
1479     /* then, set all of the userids to 0 */
1480     if (setuid(0) == -1) return 0;
1481 #endif
1482     return 1;
1483 }
1484
1485 char *
1486 base64_decode_alloc_string(
1487     char *in)
1488 {
1489     char   *out;
1490     size_t  in_len = strlen(in);
1491     size_t  out_len = 3 * (in_len / 4) + 3;
1492
1493     out = malloc(out_len);
1494     if (!base64_decode(in, in_len, out, &out_len)) {
1495         amfree(out);
1496         return NULL;
1497     }
1498     out[out_len] = '\0';
1499
1500     return out;
1501 }
1502
1503
1504 /* A GHFunc (callback for g_hash_table_foreach),
1505  * Store a property and it's value in an ARGV.
1506  *
1507  * @param key_p: (char *) property name.
1508  * @param value_p: (GSList *) property values list.
1509  * @param user_data_p: (char ***) pointer to ARGV.
1510  */
1511 static void
1512 proplist_add_to_argv(
1513     gpointer key_p,
1514     gpointer value_p,
1515     gpointer user_data_p)
1516 {
1517     char         *property_s = key_p;
1518     property_t   *value_s = value_p;
1519     GPtrArray    *argv_ptr = user_data_p;
1520     GSList       *value;
1521     char         *q, *w, *qprop;
1522
1523     q = stralloc(property_s);
1524     /* convert to lower case */
1525     for (w=q; *w != '\0'; w++) {
1526         *w = tolower(*w);
1527         if (*w == '_')
1528             *w = '-';
1529     }
1530     qprop = stralloc2("--", q);
1531     amfree(q);
1532     for(value=value_s->values; value != NULL; value = value->next) {
1533         g_ptr_array_add(argv_ptr, stralloc(qprop));
1534         g_ptr_array_add(argv_ptr, stralloc((char *)value->data));
1535     }
1536     amfree(qprop);
1537 }
1538
1539 void
1540 property_add_to_argv(
1541     GPtrArray  *argv_ptr,
1542     GHashTable *proplist)
1543 {
1544     g_hash_table_foreach(proplist, &proplist_add_to_argv, argv_ptr);
1545 }
1546
1547
1548 /*
1549  * Process parameters
1550  */
1551
1552 static char *pname = NULL;
1553 static char *ptype = NULL;
1554 static pcontext_t pcontext = CONTEXT_DEFAULT; 
1555
1556 void
1557 set_pname(char *p)
1558 {
1559     pname = newstralloc(pname, p);
1560 }
1561
1562 char *
1563 get_pname(void)
1564 {
1565     if (!pname) pname = stralloc("unknown");
1566     return pname;
1567 }
1568
1569 void
1570 set_ptype(char *p)
1571 {
1572     ptype = newstralloc(ptype, p);
1573 }
1574
1575 char *
1576 get_ptype(void)
1577 {
1578     if (!ptype) ptype = stralloc("unknown");
1579     return ptype;
1580 }
1581
1582 void
1583 set_pcontext(pcontext_t pc)
1584 {
1585     pcontext = pc;
1586 }
1587
1588 pcontext_t
1589 get_pcontext(void)
1590 {
1591     return pcontext;
1592 }
1593
1594 #ifdef __OpenBSD__
1595 void
1596 openbsd_fd_inform(void)
1597 {
1598     int i;
1599     for (i = DATA_FD_OFFSET; i < DATA_FD_OFFSET + DATA_FD_COUNT*2; i++) {
1600         /* a simple fcntl() will cause the library to "look" at this file
1601          * descriptor, which is good enough */
1602         (void)fcntl(i, F_GETFL);
1603     }
1604 }
1605 #endif
1606
1607 void
1608 debug_executing(
1609     GPtrArray *argv_ptr)
1610 {
1611     guint i;
1612     char *cmdline = stralloc((char *)g_ptr_array_index(argv_ptr, 0));
1613
1614     for (i = 1; i < argv_ptr->len-1; i++) {
1615         char *arg = g_shell_quote((char *)g_ptr_array_index(argv_ptr, i));
1616         cmdline = vstrextend(&cmdline, " ", arg, NULL);
1617         amfree(arg);
1618     }
1619     g_debug("Executing: %s\n", cmdline);
1620     amfree(cmdline);
1621 }
1622
1623 char *
1624 get_first_line(
1625     GPtrArray *argv_ptr)
1626 {
1627     char *output_string = NULL;
1628     int   inpipe[2], outpipe[2], errpipe[2];
1629     int   pid;
1630     FILE *out, *err;
1631
1632     assert(argv_ptr != NULL);
1633     assert(argv_ptr->pdata != NULL);
1634     assert(argv_ptr->len >= 1);
1635
1636     if (pipe(inpipe) == -1) {
1637         error(_("error [open pipe: %s]"), strerror(errno));
1638         /*NOTREACHED*/
1639     }
1640     if (pipe(outpipe) == -1) {
1641         error(_("error [open pipe: %s]"), strerror(errno));
1642         /*NOTREACHED*/
1643     }
1644     if (pipe(errpipe) == -1) {
1645         error(_("error [open pipe: %s]"), strerror(errno));
1646         /*NOTREACHED*/
1647     }
1648
1649     fflush(stdout);
1650     switch(pid = fork()) {
1651     case -1:
1652         error(_("error [fork: %s]"), strerror(errno));
1653         /*NOTREACHED*/
1654
1655     default:    /* parent process */
1656         aclose(inpipe[0]);
1657         aclose(outpipe[1]);
1658         aclose(errpipe[1]);
1659         break;
1660
1661     case 0: /* child process */
1662         aclose(inpipe[1]);
1663         aclose(outpipe[0]);
1664         aclose(errpipe[0]);
1665
1666         dup2(inpipe[0], 0);
1667         dup2(outpipe[1], 1);
1668         dup2(errpipe[1], 2);
1669
1670         debug_executing(argv_ptr);
1671         g_fprintf(stdout, "unknown\n");
1672         execv((char *)*argv_ptr->pdata, (char **)argv_ptr->pdata);
1673         error(_("error [exec %s: %s]"), (char *)*argv_ptr->pdata, strerror(errno));
1674     }
1675
1676     aclose(inpipe[1]);
1677
1678     out = fdopen(outpipe[0],"r");
1679     err = fdopen(errpipe[0],"r");
1680
1681     output_string = agets(out);
1682     if (!output_string)
1683         output_string = agets(err);
1684
1685     fclose(out);
1686     fclose(err);
1687
1688     waitpid(pid, NULL, 0);
1689
1690     return output_string;
1691 }
1692