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