1c1856a94119e3bddd34cc8fbdbc83454e5c7070
[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 <regex.h>
33 #include "arglist.h"
34 #include "clock.h"
35 #include "sockaddr-util.h"
36 #include "conffile.h"
37 #include "base64.h"
38
39 static int make_socket(sa_family_t family);
40 static int connect_port(sockaddr_union *addrp, in_port_t port, char *proto,
41                         sockaddr_union *svaddr, int nonblock);
42
43 static int
44 make_socket(
45     sa_family_t family)
46 {
47     int s;
48     int save_errno;
49 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
50     int on=1;
51     int r;
52 #endif
53
54     g_debug("make_socket opening socket with family %d", family);
55     s = socket(family, SOCK_STREAM, 0);
56     if (s == -1) {
57         save_errno = errno;
58         dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno));
59         errno = save_errno;
60         return -1;
61     }
62     if (s < 0 || s >= (int)FD_SETSIZE) {
63         aclose(s);
64         errno = EMFILE;                         /* out of range */
65         return -1;
66     }
67
68 #ifdef USE_REUSEADDR
69     r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
70     if (r < 0) {
71         save_errno = errno;
72         dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"),
73                   strerror(errno));
74         errno = save_errno;
75     }
76 #endif
77
78 #ifdef SO_KEEPALIVE
79     r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
80                    (void *)&on, SIZEOF(on));
81     if (r == -1) {
82         save_errno = errno;
83         dbprintf(_("make_socket: setsockopt() failed: %s\n"),
84                   strerror(save_errno));
85         aclose(s);
86         errno = save_errno;
87         return -1;
88     }
89 #endif
90
91     return s;
92 }
93
94 /* addrp is my address */
95 /* svaddr is the address of the remote machine */
96 /* return socket on success */
97 /* return -1     on failure */
98 int
99 connect_portrange(
100     sockaddr_union *addrp,
101     in_port_t           first_port,
102     in_port_t           last_port,
103     char *              proto,
104     sockaddr_union *svaddr,
105     int                 nonblock)
106 {
107     int                 s;
108     in_port_t           port;
109     static in_port_t    port_in_use[1024];
110     static int          nb_port_in_use = 0;
111     int                 i;
112     int                 save_errno = EAGAIN;
113
114     assert(first_port <= last_port);
115     /* Try a port already used */
116     for(i=0; i < nb_port_in_use; i++) {
117         port = port_in_use[i];
118         if(port >= first_port && port <= last_port) {
119             s = connect_port(addrp, port, proto, svaddr, nonblock);
120             if(s == -2) return -1;
121             if(s > 0) {
122                 return s;
123             }
124             if (errno != EAGAIN && errno != EBUSY)
125                 save_errno = errno;
126         }
127     }
128
129     /* Try a port in the range */
130     for (port = first_port; port <= last_port; port++) {
131         s = connect_port(addrp, port, proto, svaddr, nonblock);
132         if(s == -2) return -1;
133         if(s > 0) {
134             port_in_use[nb_port_in_use++] = port;
135             return s;
136         }
137         if (errno != EAGAIN && errno != EBUSY)
138             save_errno = errno;
139     }
140
141     dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
142               first_port,
143               last_port);
144     errno = save_errno;
145     return -1;
146 }
147
148 /* addrp is my address */
149 /* svaddr is the address of the remote machine */
150 /* return -2: Don't try again */
151 /* return -1: Try with another port */
152 /* return >0: this is the connected socket */
153 int
154 connect_port(
155     sockaddr_union *addrp,
156     in_port_t           port,
157     char *              proto,
158     sockaddr_union *svaddr,
159     int                 nonblock)
160 {
161     int                 save_errno;
162     struct servent *    servPort;
163     socklen_t_equiv     len;
164     socklen_t_equiv     socklen;
165     int                 s;
166
167     servPort = getservbyport((int)htons(port), proto);
168     if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
169         dbprintf(_("connect_port: Skip port %d: owned by %s.\n"),
170                   port, servPort->s_name);
171         errno = EBUSY;
172         return -1;
173     }
174
175     if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2;
176
177     SU_SET_PORT(addrp, port);
178     socklen = SS_LEN(addrp);
179     if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
180         save_errno = errno;
181         aclose(s);
182         if(servPort == NULL) {
183             dbprintf(_("connect_port: Try  port %d: available - %s\n"),
184                      port, strerror(errno));
185         } else {
186             dbprintf(_("connect_port: Try  port %d: owned by %s - %s\n"),
187                      port, servPort->s_name, strerror(errno));
188         }
189         if (save_errno != EADDRINUSE) {
190             errno = save_errno;
191             return -2;
192         }
193
194         errno = save_errno;
195         return -1;
196     }
197     if(servPort == NULL) {
198         dbprintf(_("connect_port: Try  port %d: available - Success\n"), port);
199     } else {
200         dbprintf(_("connect_port: Try  port %d: owned by %s - Success\n"),
201                   port, servPort->s_name);
202     }
203
204     /* find out what port was actually used */
205
206     len = sizeof(*addrp);
207     if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
208         save_errno = errno;
209         dbprintf(_("connect_port: getsockname() failed: %s\n"),
210                   strerror(save_errno));
211         aclose(s);
212         errno = save_errno;
213         return -1;
214     }
215
216     if (nonblock)
217         fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
218     if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
219         save_errno = errno;
220         dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
221                   str_sockaddr(addrp),
222                   strerror(save_errno));
223         dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
224                   str_sockaddr(svaddr),
225                   strerror(save_errno));
226         aclose(s);
227         errno = save_errno;
228         if (save_errno == ECONNREFUSED ||
229             save_errno == EHOSTUNREACH ||
230             save_errno == ENETUNREACH ||
231             save_errno == ETIMEDOUT)  {
232             return -2   ;
233         }
234         return -1;
235     }
236
237     dbprintf(_("connected to %s\n"),
238               str_sockaddr(svaddr));
239     dbprintf(_("our side is %s\n"),
240               str_sockaddr(addrp));
241     return s;
242 }
243
244
245 /*
246  * Bind to a port in the given range.  Takes a begin,end pair of port numbers.
247  *
248  * Returns negative on error (EGAIN if all ports are in use).  Returns 0
249  * on success.
250  */
251 int
252 bind_portrange(
253     int                 s,
254     sockaddr_union *addrp,
255     in_port_t           first_port,
256     in_port_t           last_port,
257     char *              proto)
258 {
259     in_port_t port;
260     in_port_t cnt;
261     socklen_t_equiv socklen;
262     struct servent *servPort;
263     const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
264     int save_errno = EAGAIN;
265
266     assert(first_port <= last_port);
267
268     /*
269      * We pick a different starting port based on our pid and the current
270      * time to avoid always picking the same reserved port twice.
271      */
272     port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
273
274     /*
275      * Scan through the range, trying all available ports that are either 
276      * not taken in /etc/services or registered for *amanda*.  Wrap around
277      * if we don't happen to start at the beginning.
278      */
279     for (cnt = 0; cnt < num_ports; cnt++) {
280         servPort = getservbyport((int)htons(port), proto);
281         if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
282             SU_SET_PORT(addrp, port);
283             socklen = SS_LEN(addrp);
284             if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) {
285                 if (servPort == NULL) {
286                     dbprintf(_("bind_portrange2: Try  port %d: Available - Success\n"), port);
287                 } else {
288                     dbprintf(_("bind_portrange2: Try  port %d: Owned by %s - Success.\n"), port, servPort->s_name);
289                 }
290                 return 0;
291             }
292             if (errno != EAGAIN && errno != EBUSY)
293                 save_errno = errno;
294             if (servPort == NULL) {
295                 dbprintf(_("bind_portrange2: Try  port %d: Available - %s\n"),
296                         port, strerror(errno));
297             } else {
298                 dbprintf(_("bind_portrange2: Try  port %d: Owned by %s - %s\n"),
299                         port, servPort->s_name, strerror(errno));
300             }
301         } else {
302                 dbprintf(_("bind_portrange2: Skip port %d: Owned by %s.\n"),
303                       port, servPort->s_name);
304         }
305         if (++port > last_port)
306             port = first_port;
307     }
308     dbprintf(_("bind_portrange: all ports between %d and %d busy\n"),
309                   first_port,
310                   last_port);
311     errno = save_errno;
312     return -1;
313 }
314
315 /*
316  * Writes out the entire iovec
317  */
318 ssize_t
319 full_writev(
320     int                 fd,
321     struct iovec *      iov,
322     int                 iovcnt)
323 {
324     ssize_t delta, n, total;
325
326     assert(iov != NULL);
327
328     total = 0;
329     while (iovcnt > 0) {
330         /*
331          * Write the iovec
332          */
333         n = writev(fd, iov, iovcnt);
334         if (n < 0) {
335             if (errno != EINTR)
336                 return (-1);
337         }
338         else if (n == 0) {
339             errno = EIO;
340             return (-1);
341         } else {
342             total += n;
343             /*
344              * Iterate through each iov.  Figure out what we still need
345              * to write out.
346              */
347             for (; n > 0; iovcnt--, iov++) {
348                 /* 'delta' is the bytes written from this iovec */
349                 delta = ((size_t)n < (size_t)iov->iov_len) ? n : (ssize_t)iov->iov_len;
350                 /* subtract from the total num bytes written */
351                 n -= delta;
352                 assert(n >= 0);
353                 /* subtract from this iovec */
354                 iov->iov_len -= delta;
355                 iov->iov_base = (char *)iov->iov_base + delta;
356                 /* if this iovec isn't empty, run the writev again */
357                 if (iov->iov_len > 0)
358                     break;
359             }
360         }
361     }
362     return (total);
363 }
364
365
366 int
367 needs_quotes(
368     const char * str)
369 {
370     return (match("[ \t\f\r\n\"]", str) != 0);
371 }
372
373
374 /*
375  * For backward compatibility we are trying for minimal quoting.
376  * We only quote a string if it contains whitespace or is misquoted...
377  */
378
379 char *
380 quote_string(
381     const char *str)
382 {
383     char *  s;
384     char *  ret;
385
386     if ((str == NULL) || (*str == '\0')) {
387         ret = stralloc("\"\"");
388     } else if ((match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) {
389         /*
390          * String does not need to be quoted since it contains
391          * neither whitespace, control or quote characters.
392          */
393         ret = stralloc(str);
394     } else {
395         /*
396          * Allocate maximum possible string length.
397          * (a string of all quotes plus room for leading ", trailing " and NULL)
398          */
399         ret = s = alloc((strlen(str) * 2) + 2 + 1);
400         *(s++) = '"';
401         while (*str != '\0') {
402             if (*str == '\t') {
403                 *(s++) = '\\';
404                 *(s++) = 't';
405                 str++;
406                 continue;
407             } else if (*str == '\n') {
408                 *(s++) = '\\';
409                 *(s++) = 'n';
410                 str++;
411                 continue;
412             } else if (*str == '\r') {
413                 *(s++) = '\\';
414                 *(s++) = 'r';
415                 str++;
416                 continue;
417             } else if (*str == '\f') {
418                 *(s++) = '\\';
419                 *(s++) = 'f';
420                 str++;
421                 continue;
422             } else if (*str == '\\') {
423                 *(s++) = '\\';
424                 *(s++) = '\\';
425                 str++;
426                 continue;
427             }
428             if (*str == '"')
429                 *(s++) = '\\';
430             *(s++) = *(str++);
431         }
432         *(s++) = '"';
433         *s = '\0';
434     }
435     return (ret);
436 }
437
438
439 char *
440 unquote_string(
441     const char *str)
442 {
443     char * ret;
444
445     if ((str == NULL) || (*str == '\0')) {
446         ret = stralloc("");
447     } else {
448         char * in;
449         char * out;
450
451         ret = in = out = stralloc(str);
452         while (*in != '\0') {
453             if (*in == '"') {
454                 in++;
455                 continue;
456             }
457
458             if (*in == '\\') {
459                 in++;
460                 if (*in == 'n') {
461                     in++;
462                     *(out++) = '\n';
463                     continue;
464                 } else if (*in == 't') {
465                     in++;
466                     *(out++) = '\t';
467                     continue;
468                 } else if (*in == 'r') {
469                     in++;
470                     *(out++) = '\r';
471                     continue;
472                 } else if (*in == 'f') {
473                     in++;
474                     *(out++) = '\f';
475                     continue;
476                 } else if (*in >= '0' && *in <= '7') {
477                     char c = 0;
478                     int i = 0;
479
480                     while (i < 3 && *in >= '0' && *in <= '7') {
481                         c = (c << 3) + *(in++) - '0';
482                         i++;
483                     }
484                     if (c)
485                         *(out++) = c;
486                 } else if (*in == '\0') {
487                     /* trailing backslash -- ignore */
488                     break;
489                 }
490             }
491             *(out++) = *(in++);
492         }
493         *out = '\0';
494     }
495     return (ret);
496 }
497
498 gchar **
499 split_quoted_strings(
500     const gchar *string)
501 {
502     char *local = g_strdup(string);
503     char *start = local;
504     char *p = local;
505     char **result;
506     GPtrArray *strs = g_ptr_array_new();
507     int iq = 0;
508
509     while (*p) {
510         if (!iq && *p == ' ') {
511             *p = '\0';
512             g_ptr_array_add(strs, unquote_string(start));
513             start = p+1;
514         } else if (*p == '\\') {
515             /* next character is taken literally; if it's a multicharacter
516              * escape (e.g., \171), that doesn't bother us here */
517             p++;
518             if (!*p) break;
519         } else if (*p == '\"') {
520             iq = ! iq;
521         }
522
523         p++;
524     }
525     if (start != string)
526         g_ptr_array_add(strs, unquote_string(start));
527
528     /* now convert strs into a strv, by stealing its references to the underlying
529      * strings */
530     result = g_new0(char *, strs->len + 1);
531     memmove(result, strs->pdata, sizeof(char *) * strs->len);
532
533     g_ptr_array_free(strs, FALSE); /* FALSE => don't free strings */
534     g_free(local);
535
536     return result;
537 }
538
539 char *
540 strquotedstr(char **saveptr)
541 {
542     char *  tok = strtok_r(NULL, " ", saveptr);
543     size_t      len;
544     int         in_quote;
545     int         in_backslash;
546     char       *p, *t;
547
548     if (!tok)
549         return tok;
550     len = strlen(tok);
551     in_quote = 0;
552     in_backslash = 0;
553     p = tok;
554     while (in_quote || in_backslash || *p != '\0') {
555         if (*p == '\0') {
556             /* append a new token */
557             t = strtok_r(NULL, " ", saveptr);
558             if (!t)
559                 return NULL;
560             tok[len] = ' ';
561             len = strlen(tok);
562         }
563         if (!in_backslash) {
564             if (*p == '"')
565                 in_quote = !in_quote;
566             else if (*p == '\\') {
567                 in_backslash = 1;
568             }
569         } else {
570            in_backslash = 0;
571         }
572         p++;
573     }
574     return tok;
575 }
576
577 char *
578 sanitize_string(
579     const char *str)
580 {
581     char * s;
582     char * ret;
583
584     if ((str == NULL) || (*str == '\0')) {
585         ret = stralloc("");
586     } else {
587         ret = stralloc(str);
588         for (s = ret; *s != '\0'; s++) {
589             if (iscntrl((int)*s))
590                 *s = '?';
591         }
592     }
593     return (ret);
594 }
595
596 /*
597    Return 0 if the following characters are present
598    * ( ) < > [ ] , ; : ! $ \ / "
599    else returns 1
600 */
601
602 int
603 validate_mailto(
604     const char *mailto)
605 {
606     return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
607 }
608
609 int copy_file(
610     char  *dst,
611     char  *src,
612     char **errmsg)
613 {
614     int     infd, outfd;
615     int     save_errno;
616     size_t nb;
617     char    buf[32768];
618     char   *quoted;
619
620     if ((infd = open(src, O_RDONLY)) == -1) {
621         save_errno = errno;
622         quoted = quote_string(src);
623         *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
624                             quoted, strerror(save_errno));
625         amfree(quoted);
626         return -1;
627     }
628
629     if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
630         save_errno = errno;
631         quoted = quote_string(dst);
632         *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
633                             quoted, strerror(save_errno));
634         amfree(quoted);
635         close(infd);
636         return -1;
637     }
638
639     while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
640         if(full_write(outfd,&buf,nb) < nb) {
641             save_errno = errno;
642             quoted = quote_string(dst);
643             *errmsg = vstrallocf(_("Error writing to '%s': %s"),
644                                 quoted, strerror(save_errno));
645             amfree(quoted);
646             close(infd);
647             close(outfd);
648             return -1;
649         }
650     }
651
652     if (errno != 0) {
653         save_errno = errno;
654         quoted = quote_string(src);
655         *errmsg = vstrallocf(_("Error reading from '%s': %s"),
656                             quoted, strerror(save_errno));
657         amfree(quoted);
658         close(infd);
659         close(outfd);
660         return -1;
661     }
662
663     close(infd);
664     close(outfd);
665     return 0;
666 }
667
668 #ifndef HAVE_READLINE
669 /*
670  * simple readline() replacements, used when we don't have readline
671  * support from the system.
672  */
673
674 char *
675 readline(
676     const char *prompt)
677 {
678     g_printf("%s", prompt);
679     fflush(stdout);
680     fflush(stderr);
681     return agets(stdin);
682 }
683
684 void 
685 add_history(
686     const char *line)
687 {
688     (void)line;         /* Quiet unused parameter warning */
689 }
690
691 #endif
692
693 /* Order of preference: readdir64(), readdir(). */
694 #if HAVE_DECL_READDIR64
695 #  define USE_DIRENT64
696 #  define USE_READDIR64
697 #elif HAVE_DECL_READDIR
698 #  define USE_READDIR
699 #else
700 # error No readdir() or readdir64() available!
701 #endif
702
703 char * portable_readdir(DIR* handle) {
704
705 #ifdef USE_DIRENT64
706     struct dirent64 *entry_p;
707 #else
708     struct dirent *entry_p;
709 #endif
710
711     static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
712
713     g_static_mutex_lock(&mutex);
714
715 #ifdef USE_READDIR
716     entry_p = readdir(handle);
717 #endif
718 #ifdef USE_READDIR64
719     entry_p = readdir64(handle);
720 #endif
721
722     g_static_mutex_unlock(&mutex);
723     
724     if (entry_p == NULL)
725         return NULL;
726
727     /* FIXME: According to glibc documentation, d_name may not be
728        null-terminated in some cases on some very old platforms. Not
729        sure what to do about that case. */
730     return strdup(entry_p->d_name);
731 }
732
733 int search_directory(DIR * handle, const char * regex,
734                      SearchDirectoryFunctor functor, gpointer user_data) {
735     int rval = 0;
736     regex_t compiled_regex;
737     gboolean done = FALSE;
738
739     if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
740         regfree(&compiled_regex);
741         return -1;
742     }
743
744     rewinddir(handle);
745
746     while (!done) {
747         char * read_name;
748         int result;
749         read_name = portable_readdir(handle);
750         if (read_name == NULL) {
751             regfree(&compiled_regex);
752             return rval;
753         }
754         result = regexec(&compiled_regex, read_name, 0, NULL, 0);
755         if (result == 0) {
756             rval ++;
757             done = !functor(read_name, user_data);
758         }
759         amfree(read_name);
760     }
761     regfree(&compiled_regex);
762     return rval;
763 }
764
765 char* find_regex_substring(const char* base_string, const regmatch_t match) {
766     char * rval;
767     int size;
768
769     size = match.rm_eo - match.rm_so;
770     rval = malloc(size+1);
771     memcpy(rval, base_string + match.rm_so, size);
772     rval[size] = '\0';
773
774     return rval;
775 }
776
777 int compare_possibly_null_strings(const char * a, const char * b) {
778     if (a == b) {
779         /* NULL or otherwise, they're the same. */
780         return 0;
781     } else if (a == NULL) {
782         /* b != NULL */
783         return -1;
784     } else if (b == NULL) {
785         /* a != NULL */
786         return 1;
787     } else {
788         /* a != NULL != b */
789         return strcmp(a, b);
790     }
791 }
792
793 int
794 resolve_hostname(const char *hostname,
795         int socktype,
796         struct addrinfo **res,
797         char **canonname)
798 {
799     struct addrinfo hints;
800     struct addrinfo *myres;
801     int flags = 0;
802     int result;
803
804     if (res) *res = NULL;
805     if (canonname) {
806         *canonname = NULL;
807         flags = AI_CANONNAME;
808     }
809
810 #ifdef AI_ADDRCONFIG
811     flags |= AI_ADDRCONFIG;
812 #endif
813
814     memset(&hints, 0, sizeof(hints));
815 #ifdef WORKING_IPV6
816     /* get any kind of addresss */
817     hints.ai_family = AF_UNSPEC;
818 #else
819     /* even if getaddrinfo supports IPv6, don't let it return
820      * such an address */
821     hints.ai_family = AF_INET;
822 #endif
823     hints.ai_flags = flags;
824     hints.ai_socktype = socktype;
825     result = getaddrinfo(hostname, NULL, &hints, &myres);
826     if (result != 0) {
827         return result;
828     }
829
830     if (canonname && myres && myres->ai_canonname) {
831         *canonname = stralloc(myres->ai_canonname);
832     }
833
834     if (res) {
835         *res = myres;
836     } else {
837         freeaddrinfo(myres);
838     }
839
840     return result;
841 }
842
843 char *
844 _str_exit_status(
845     char *subject,
846     amwait_t status)
847 {
848     if (WIFEXITED(status)) {
849         int exitstatus = WEXITSTATUS(status);
850         if (exitstatus == 0)
851             return vstrallocf(_("%s exited normally"), subject);
852         else
853             return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
854     }
855
856     if (WIFSIGNALED(status)) {
857         int signal = WTERMSIG(status);
858 #ifdef WCOREDUMP
859         if (WCOREDUMP(status))
860             return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
861                 subject, signal);
862         else
863 #endif
864             return vstrallocf(_("%s exited after receiving signal %d"),
865                 subject, signal);
866     }
867
868     if (WIFSTOPPED(status)) {
869         int signal = WSTOPSIG(status);
870         return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
871             subject, signal);
872     }
873
874 #ifdef WIFCONTINUED
875     if (WIFCONTINUED(status)) {
876         return vstrallocf(_("%s was resumed"), subject);
877     }
878 #endif
879
880     return vstrallocf(_("%s exited in unknown circumstances"), subject);
881 }
882
883 void
884 check_running_as(running_as_flags who)
885 {
886 #ifdef CHECK_USERID
887     struct passwd *pw;
888     uid_t uid_me;
889     uid_t uid_target;
890     char *uname_me = NULL;
891     char *uname_target = NULL;
892     char *dumpuser;
893
894     uid_me = getuid();
895     if ((pw = getpwuid(uid_me)) == NULL) {
896         error(_("current userid %ld not found in password database"), (long)uid_me);
897         /* NOTREACHED */
898     }
899     uname_me = stralloc(pw->pw_name);
900
901 #ifndef SINGLE_USERID
902     if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
903         error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
904                 (long long int)geteuid(), (long long int)uid_me);
905         /* NOTREACHED */
906     }
907 #endif
908
909     switch (who & RUNNING_AS_USER_MASK) {
910         case RUNNING_AS_ANY:
911             uid_target = uid_me;
912             uname_target = uname_me;
913             return;
914
915         case RUNNING_AS_ROOT:
916             uid_target = 0;
917             uname_target = "root";
918             break;
919
920         case RUNNING_AS_DUMPUSER_PREFERRED:
921             dumpuser = getconf_str(CNF_DUMPUSER);
922             if ((pw = getpwnam(dumpuser)) != NULL &&
923                     uid_me != pw->pw_uid) {
924                 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
925                     uid_me == pw->pw_uid) {
926                     /* uid == CLIENT_LOGIN: not ideal, but OK */
927                     dbprintf(_("NOTE: running as '%s', which is the client"
928                                " user, not the dumpuser ('%s'); forging"
929                                " on anyway\n"),
930                              CLIENT_LOGIN, dumpuser);
931                     uid_target = uid_me; /* force success below */
932                    break;
933                 }
934             }
935             /* FALLTHROUGH */
936
937         case RUNNING_AS_DUMPUSER:
938             uname_target = getconf_str(CNF_DUMPUSER);
939             if ((pw = getpwnam(uname_target)) == NULL) {
940                 error(_("cannot look up dumpuser \"%s\""), uname_target);
941                 /*NOTREACHED*/
942             }
943             uid_target = pw->pw_uid;
944             break;
945
946         case RUNNING_AS_CLIENT_LOGIN:
947             uname_target = CLIENT_LOGIN;
948             if ((pw = getpwnam(uname_target)) == NULL) {
949                 error(_("cannot look up client user \"%s\""), uname_target);
950                 /*NOTREACHED*/
951             }
952             uid_target = pw->pw_uid;
953             break;
954
955         default:
956             error(_("Unknown check_running_as() call"));
957             /* NOTREACHED */
958     }
959
960     if (uid_me != uid_target) {
961         error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
962         /*NOTREACHED*/
963     }
964     amfree(uname_me);
965
966 #else
967     /* Quiet unused variable warning */
968     (void)who;
969 #endif
970 }
971
972 int
973 set_root_privs(int need_root)
974 {
975 #ifndef SINGLE_USERID
976     if (need_root) {
977         if (seteuid(0) == -1) return 0;
978         /* (we don't switch the group back) */
979     } else {
980         if (geteuid() != 0) return 0;
981         if (seteuid(getuid()) == -1) return 0;
982         if (setegid(getgid()) == -1) return 0;
983     }
984 #else
985     (void)need_root; /* Quiet unused variable warning */
986 #endif
987     return 1;
988 }
989
990 int
991 become_root(void)
992 {
993 #ifndef SINGLE_USERID
994     // if euid !=0, it set only euid
995     if (setuid(0) == -1) return 0;
996     // will set ruid because euid == 0.
997     if (setuid(0) == -1) return 0;
998 #endif
999     return 1;
1000 }
1001
1002
1003 char *
1004 base64_decode_alloc_string(
1005     char *in)
1006 {
1007     char   *out;
1008     size_t  in_len = strlen(in);
1009     size_t  out_len = 3 * (in_len / 4) + 3;
1010
1011     out = malloc(out_len);
1012     if (!base64_decode(in, in_len, out, &out_len)) {
1013         amfree(out);
1014         return NULL;
1015     }
1016     out[out_len] = '\0';
1017
1018     return out;
1019 }
1020
1021
1022 /* A GHFunc (callback for g_hash_table_foreach) */
1023 void count_proplist(
1024     gpointer key_p G_GNUC_UNUSED,
1025     gpointer value_p,
1026     gpointer user_data_p)
1027 {
1028     property_t *value_s = value_p;
1029     int    *nb = user_data_p;
1030     GSList  *value;
1031
1032     for(value=value_s->values; value != NULL; value = value->next) {
1033         (*nb)++;
1034     }
1035 }
1036
1037 /* A GHFunc (callback for g_hash_table_foreach) */
1038 void proplist_add_to_argv(
1039     gpointer key_p,
1040     gpointer value_p,
1041     gpointer user_data_p)
1042 {
1043     char         *property_s = key_p;
1044     property_t   *value_s = value_p;
1045     char       ***argv = user_data_p;
1046     GSList       *value;
1047     char         *q, *w, *qprop;
1048
1049     q = stralloc(property_s);
1050     /* convert to lower case */
1051     for (w=q; *w != '\0'; w++) {
1052         *w = tolower(*w);
1053         if (*w == '_')
1054             *w = '-';
1055     }
1056     qprop = stralloc2("--", q);
1057     amfree(q);
1058     for(value=value_s->values; value != NULL; value = value->next) {
1059         **argv = stralloc(qprop);
1060         (*argv)++;
1061         **argv = stralloc((char *)value->data);
1062         (*argv)++;
1063     }
1064     amfree(qprop);
1065 }
1066
1067
1068 /*
1069  * Process parameters
1070  */
1071
1072 static char *pname = NULL;
1073 static char *ptype = NULL;
1074 static pcontext_t pcontext = CONTEXT_DEFAULT; 
1075
1076 void
1077 set_pname(char *p)
1078 {
1079     pname = newstralloc(pname, p);
1080 }
1081
1082 char *
1083 get_pname(void)
1084 {
1085     if (!pname) pname = stralloc("unknown");
1086     return pname;
1087 }
1088
1089 void
1090 set_ptype(char *p)
1091 {
1092     ptype = newstralloc(ptype, p);
1093 }
1094
1095 char *
1096 get_ptype(void)
1097 {
1098     if (!ptype) ptype = stralloc("unknown");
1099     return ptype;
1100 }
1101
1102 void
1103 set_pcontext(pcontext_t pc)
1104 {
1105     pcontext = pc;
1106 }
1107
1108 pcontext_t
1109 get_pcontext(void)
1110 {
1111     return pcontext;
1112 }