X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Futil.c;h=eed9d981bca4bc5283453f532e281412de548788;hb=4f0b86f7a23848c16cfe82fae81e639917fcff27;hp=06c4de22911c8dcc14ff0af35d31950620894224;hpb=a6127998ee6dcab6bb034f6ca985b07804a86f9a;p=debian%2Famanda diff --git a/common-src/util.c b/common-src/util.c index 06c4de2..eed9d98 100644 --- a/common-src/util.c +++ b/common-src/util.c @@ -29,81 +29,21 @@ #include "amanda.h" #include "util.h" +#include "match.h" #include #include "arglist.h" #include "clock.h" #include "sockaddr-util.h" #include "conffile.h" - -#ifdef HAVE_LIBCURL -#include -#endif +#include "base64.h" +#include "stream.h" +#include "pipespawn.h" +#include +#include static int make_socket(sa_family_t family); -static int connect_port(struct sockaddr_storage *addrp, in_port_t port, char *proto, - struct sockaddr_storage *svaddr, int nonblock); - -/* - * Keep calling read() until we've read buflen's worth of data, or EOF, - * or we get an error. - * - * Returns the number of bytes read, 0 on EOF, or negative on error. - */ -ssize_t -fullread( - int fd, - void * vbuf, - size_t buflen) -{ - ssize_t nread, tot = 0; - char *buf = vbuf; /* cast to char so we can ++ it */ - - while (buflen > 0) { - nread = read(fd, buf, buflen); - if (nread < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - return ((tot > 0) ? tot : -1); - } - - if (nread == 0) - break; - - tot += nread; - buf += nread; - buflen -= nread; - } - return (tot); -} - -/* - * Keep calling write() until we've written buflen's worth of data, - * or we get an error. - * - * Returns the number of bytes written, or negative on error. - */ -ssize_t -fullwrite( - int fd, - const void *vbuf, - size_t buflen) -{ - ssize_t nwritten, tot = 0; - const char *buf = vbuf; /* cast to char so we can ++ it */ - - while (buflen > 0) { - nwritten = write(fd, buf, buflen); - if (nwritten < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - return ((tot > 0) ? tot : -1); - } - tot += nwritten; - buf += nwritten; - buflen -= nwritten; - } - return (tot); -} +static int connect_port(sockaddr_union *addrp, in_port_t port, char *proto, + sockaddr_union *svaddr, int nonblock); static int make_socket( @@ -116,6 +56,7 @@ make_socket( int r; #endif + g_debug("make_socket opening socket with family %d", family); s = socket(family, SOCK_STREAM, 0); if (s == -1) { save_errno = errno; @@ -155,17 +96,22 @@ make_socket( return s; } +GQuark am_util_error_quark(void) +{ + return g_quark_from_static_string("am-util-error-quark"); +} + /* addrp is my address */ /* svaddr is the address of the remote machine */ /* return socket on success */ /* return -1 on failure */ int connect_portrange( - struct sockaddr_storage *addrp, + sockaddr_union *addrp, in_port_t first_port, in_port_t last_port, char * proto, - struct sockaddr_storage *svaddr, + sockaddr_union *svaddr, int nonblock) { int s; @@ -216,16 +162,16 @@ connect_portrange( /* return >0: this is the connected socket */ int connect_port( - struct sockaddr_storage *addrp, + sockaddr_union *addrp, in_port_t port, char * proto, - struct sockaddr_storage *svaddr, + sockaddr_union *svaddr, int nonblock) { int save_errno; struct servent * servPort; - socklen_t len; - socklen_t socklen; + socklen_t_equiv len; + socklen_t_equiv socklen; int s; servPort = getservbyport((int)htons(port), proto); @@ -236,9 +182,9 @@ connect_port( return -1; } - if ((s = make_socket(addrp->ss_family)) == -1) return -2; + if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2; - SS_SET_PORT(addrp, port); + SU_SET_PORT(addrp, port); socklen = SS_LEN(addrp); if (bind(s, (struct sockaddr *)addrp, socklen) != 0) { save_errno = errno; @@ -315,14 +261,14 @@ connect_port( int bind_portrange( int s, - struct sockaddr_storage *addrp, + sockaddr_union *addrp, in_port_t first_port, in_port_t last_port, char * proto) { in_port_t port; in_port_t cnt; - socklen_t socklen; + socklen_t_equiv socklen; struct servent *servPort; const in_port_t num_ports = (in_port_t)(last_port - first_port + 1); int save_errno = EAGAIN; @@ -343,108 +289,278 @@ bind_portrange( for (cnt = 0; cnt < num_ports; cnt++) { servPort = getservbyport((int)htons(port), proto); if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) { - SS_SET_PORT(addrp, port); + SU_SET_PORT(addrp, port); socklen = SS_LEN(addrp); if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) { if (servPort == NULL) { - dbprintf(_("bind_portrange2: Try port %d: Available - Success\n"), port); + g_debug(_("bind_portrange2: Try port %d: Available - Success"), port); } else { - dbprintf(_("bind_portrange2: Try port %d: Owned by %s - Success.\n"), port, servPort->s_name); + g_debug(_("bind_portrange2: Try port %d: Owned by %s - Success."), port, servPort->s_name); } return 0; } if (errno != EAGAIN && errno != EBUSY) save_errno = errno; if (servPort == NULL) { - dbprintf(_("bind_portrange2: Try port %d: Available - %s\n"), + g_debug(_("bind_portrange2: Try port %d: Available - %s"), port, strerror(errno)); } else { - dbprintf(_("bind_portrange2: Try port %d: Owned by %s - %s\n"), + g_debug(_("bind_portrange2: Try port %d: Owned by %s - %s"), port, servPort->s_name, strerror(errno)); } } else { - dbprintf(_("bind_portrange2: Skip port %d: Owned by %s.\n"), + g_debug(_("bind_portrange2: Skip port %d: Owned by %s."), port, servPort->s_name); } if (++port > last_port) port = first_port; } - dbprintf(_("bind_portrange: all ports between %d and %d busy\n"), + g_debug(_("bind_portrange: all ports between %d and %d busy"), first_port, last_port); errno = save_errno; return -1; } - int -needs_quotes( - const char * str) +interruptible_accept( + int sock, + struct sockaddr *addr, + socklen_t *addrlen, + gboolean (*prolong)(gpointer data), + gpointer prolong_data) { - return (match("[ \t\f\r\n\"]", str) != 0); + SELECT_ARG_TYPE readset; + struct timeval tv; + int nfound; + + if (sock < 0 || sock >= FD_SETSIZE) { + g_debug("interruptible_accept: bad socket %d", sock); + return EBADF; + } + + memset(&readset, 0, SIZEOF(readset)); + + while (1) { + if (!prolong(prolong_data)) { + errno = 0; + return -1; + } + + FD_ZERO(&readset); + FD_SET(sock, &readset); + + /* try accepting for 1s */ + memset(&tv, 0, SIZEOF(tv)); + tv.tv_sec = 1; + + nfound = select(sock+1, &readset, NULL, NULL, &tv); + if (nfound < 0) { + return -1; + } else if (nfound == 0) { + continue; + } else if (!FD_ISSET(sock, &readset)) { + g_debug("interruptible_accept: select malfunction"); + errno = EBADF; + return -1; + } else { + int rv = accept(sock, addr, addrlen); + if (rv < 0 && errno == EAGAIN) + continue; + return rv; + } + } +} + +/* + * Writes out the entire iovec + */ +ssize_t +full_writev( + int fd, + struct iovec * iov, + int iovcnt) +{ + ssize_t delta, n, total; + + assert(iov != NULL); + + total = 0; + while (iovcnt > 0) { + /* + * Write the iovec + */ + n = writev(fd, iov, iovcnt); + if (n < 0) { + if (errno != EINTR) + return (-1); + } + else if (n == 0) { + errno = EIO; + return (-1); + } else { + total += n; + /* + * Iterate through each iov. Figure out what we still need + * to write out. + */ + for (; n > 0; iovcnt--, iov++) { + /* 'delta' is the bytes written from this iovec */ + delta = ((size_t)n < (size_t)iov->iov_len) ? n : (ssize_t)iov->iov_len; + /* subtract from the total num bytes written */ + n -= delta; + assert(n >= 0); + /* subtract from this iovec */ + iov->iov_len -= delta; + iov->iov_base = (char *)iov->iov_base + delta; + /* if this iovec isn't empty, run the writev again */ + if (iov->iov_len > 0) + break; + } + } + } + return (total); } /* - * For backward compatibility we are trying for minimal quoting. - * We only quote a string if it contains whitespace or is misquoted... + * For backward compatibility we are trying for minimal quoting. Unless ALWAYS + * is true, we only quote a string if it contains whitespace or is misquoted... */ char * -quote_string( - const char *str) +quote_string_maybe( + const char *str, + gboolean always) { char * s; char * ret; if ((str == NULL) || (*str == '\0')) { ret = stralloc("\"\""); - } else if ((match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) { - /* - * String does not need to be quoted since it contains - * neither whitespace, control or quote characters. - */ - ret = stralloc(str); } else { - /* - * Allocate maximum possible string length. - * (a string of all quotes plus room for leading ", trailing " and NULL) - */ - ret = s = alloc((strlen(str) * 2) + 2 + 1); - *(s++) = '"'; - while (*str != '\0') { - if (*str == '\t') { - *(s++) = '\\'; - *(s++) = 't'; - str++; - continue; - } else if (*str == '\n') { - *(s++) = '\\'; - *(s++) = 'n'; - str++; - continue; - } else if (*str == '\r') { - *(s++) = '\\'; - *(s++) = 'r'; - str++; - continue; - } else if (*str == '\f') { - *(s++) = '\\'; - *(s++) = 'f'; - str++; - continue; - } else if (*str == '\\') { - *(s++) = '\\'; - *(s++) = '\\'; - str++; - continue; - } - if (*str == '"') - *(s++) = '\\'; - *(s++) = *(str++); + const char *r; + for (r = str; *r; r++) { + if (*r == ':' || *r == '\'' || *r == '\\' || *r == '\"' || + *r <= ' ' || *r == 0x7F ) + always = 1; + } + if (!always) { + /* + * String does not need to be quoted since it contains + * neither whitespace, control or quote characters. + */ + ret = stralloc(str); + } else { + /* + * Allocate maximum possible string length. + * (a string of all quotes plus room for leading ", trailing " and + * NULL) + */ + ret = s = alloc((strlen(str) * 2) + 2 + 1); + *(s++) = '"'; + while (*str != '\0') { + if (*str == '\t') { + *(s++) = '\\'; + *(s++) = 't'; + str++; + continue; + } else if (*str == '\n') { + *(s++) = '\\'; + *(s++) = 'n'; + str++; + continue; + } else if (*str == '\r') { + *(s++) = '\\'; + *(s++) = 'r'; + str++; + continue; + } else if (*str == '\f') { + *(s++) = '\\'; + *(s++) = 'f'; + str++; + continue; + } else if (*str == '\\') { + *(s++) = '\\'; + *(s++) = '\\'; + str++; + continue; + } + if (*str == '"') + *(s++) = '\\'; + *(s++) = *(str++); + } + *(s++) = '"'; + *s = '\0'; } - *(s++) = '"'; - *s = '\0'; + } + return (ret); +} + + +int +len_quote_string_maybe( + const char *str, + gboolean always) +{ + int ret; + + if ((str == NULL) || (*str == '\0')) { + ret = 0; + } else { + const char *r; + for (r = str; *r; r++) { + if (*r == ':' || *r == '\'' || *r == '\\' || *r == '\"' || + *r <= ' ' || *r == 0x7F ) + always = 1; + } + if (!always) { + /* + * String does not need to be quoted since it contains + * neither whitespace, control or quote characters. + */ + ret = strlen(str); + } else { + /* + * Allocate maximum possible string length. + * (a string of all quotes plus room for leading ", trailing " and + * NULL) + */ + ret = 1; + while (*str != '\0') { + if (*str == '\t') { + ret++; + ret++; + str++; + continue; + } else if (*str == '\n') { + ret++; + ret++; + str++; + continue; + } else if (*str == '\r') { + ret++; + ret++; + str++; + continue; + } else if (*str == '\f') { + ret++; + ret++; + str++; + continue; + } else if (*str == '\\') { + ret++; + ret++; + str++; + continue; + } + if (*str == '"') + ret++; + ret++; + str++; + } + ret++; + } } return (ret); } @@ -487,6 +603,19 @@ unquote_string( in++; *(out++) = '\f'; continue; + } else if (*in >= '0' && *in <= '7') { + char c = 0; + int i = 0; + + while (i < 3 && *in >= '0' && *in <= '7') { + c = (c << 3) + *(in++) - '0'; + i++; + } + if (c) + *(out++) = c; + } else if (*in == '\0') { + /* trailing backslash -- ignore */ + break; } } *(out++) = *(in++); @@ -496,6 +625,91 @@ unquote_string( return (ret); } +gchar ** +split_quoted_strings( + const gchar *string) +{ + char *local; + char *start; + char *p; + char **result; + GPtrArray *strs; + int iq = 0; + + if (!string) + return NULL; + + p = start = local = g_strdup(string); + strs = g_ptr_array_new(); + + while (*p) { + if (!iq && *p == ' ') { + *p = '\0'; + g_ptr_array_add(strs, unquote_string(start)); + start = p+1; + } else if (*p == '\\') { + /* next character is taken literally; if it's a multicharacter + * escape (e.g., \171), that doesn't bother us here */ + p++; + if (!*p) break; + } else if (*p == '\"') { + iq = ! iq; + } + + p++; + } + if (start != string) + g_ptr_array_add(strs, unquote_string(start)); + + /* now convert strs into a strv, by stealing its references to the underlying + * strings */ + result = g_new0(char *, strs->len + 1); + memmove(result, strs->pdata, sizeof(char *) * strs->len); + + g_ptr_array_free(strs, TRUE); /* TRUE => free pdata, strings are not freed */ + g_free(local); + + return result; +} + +char * +strquotedstr(char **saveptr) +{ + char * tok = strtok_r(NULL, " ", saveptr); + size_t len; + int in_quote; + int in_backslash; + char *p, *t; + + if (!tok) + return tok; + len = strlen(tok); + in_quote = 0; + in_backslash = 0; + p = tok; + while (in_quote || in_backslash || *p != '\0') { + if (*p == '\0') { + /* append a new token */ + t = strtok_r(NULL, " ", saveptr); + if (!t) + return NULL; + tok[len] = ' '; + len = strlen(tok); + } + if (!in_backslash) { + if (*p == '"') + in_quote = !in_quote; + else if (*p == '\\') { + in_backslash = 1; + } + } else { + in_backslash = 0; + } + p++; + } + return tok; +} + char * sanitize_string( const char *str) @@ -515,6 +729,316 @@ sanitize_string( return (ret); } +char *hexencode_string(const char *str) +{ + size_t orig_len, new_len, i; + GString *s; + gchar *ret; + if (!str) { + s = g_string_sized_new(0); + goto cleanup; + } + new_len = orig_len = strlen(str); + for (i = 0; i < orig_len; i++) { + if (!g_ascii_isalnum(str[i])) { + new_len += 2; + } + } + s = g_string_sized_new(new_len); + + for (i = 0; i < orig_len; i++) { + if (g_ascii_isalnum(str[i])) { + g_string_append_c(s, str[i]); + } else { + g_string_append_printf(s, "%%%02hhx", str[i]); + } + } + +cleanup: + ret = s->str; + g_string_free(s, FALSE); + return ret; +} + +char *hexdecode_string(const char *str, GError **err) +{ + size_t orig_len, new_len, i; + GString *s; + gchar *ret; + if (!str) { + s = g_string_sized_new(0); + goto cleanup; + } + new_len = orig_len = strlen(str); + for (i = 0; i < orig_len; i++) { + if (str[i] == '%') { + new_len -= 2; + } + } + s = g_string_sized_new(new_len); + + for (i = 0; (orig_len > 2) && (i < orig_len-2); i++) { + if (str[i] == '%') { + gchar tmp = 0; + size_t j; + for (j = 1; j < 3; j++) { + tmp <<= 4; + if (str[i+j] >= '0' && str[i+j] <= '9') { + tmp += str[i+j] - '0'; + } else if (str[i+j] >= 'a' && str[i+j] <= 'f') { + tmp += str[i+j] - 'a' + 10; + } else if (str[i+j] >= 'A' && str[i+j] <= 'F') { + tmp += str[i+j] - 'A' + 10; + } else { + /* error */ + g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL, + "Illegal character (non-hex) 0x%02hhx at offset %zd", str[i+j], i+j); + g_string_truncate(s, 0); + goto cleanup; + } + } + if (!tmp) { + g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL, + "Encoded NULL at starting offset %zd", i); + g_string_truncate(s, 0); + goto cleanup; + } + g_string_append_c(s, tmp); + i += 2; + } else { + g_string_append_c(s, str[i]); + } + } + for ( /*nothing*/; i < orig_len; i++) { + if (str[i] == '%') { + g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL, + "'%%' found at offset %zd, but fewer than two characters follow it (%zd)", i, orig_len-i-1); + g_string_truncate(s, 0); + goto cleanup; + } else { + g_string_append_c(s, str[i]); + } + } + +cleanup: + ret = s->str; + g_string_free(s, FALSE); + return ret; +} + +/* Helper for parse_braced_component; this will turn a single element array + * matching /^\d+\.\.\d+$/ into a sequence of numbered array elements. */ +static GPtrArray * +expand_braced_sequence(GPtrArray *arr) +{ + char *elt, *p; + char *l, *r; + int ldigits, rdigits, ndigits; + guint64 start, end; + gboolean leading_zero; + + /* check whether the element matches the pattern */ + /* expand last element of the array only */ + elt = g_ptr_array_index(arr, arr->len-1); + ldigits = 0; + for (l = p = elt; *p && g_ascii_isdigit(*p); p++) + ldigits++; + if (ldigits == 0) + return arr; + if (*(p++) != '.') + return arr; + if (*(p++) != '.') + return arr; + rdigits = 0; + for (r = p; *p && g_ascii_isdigit(*p); p++) + rdigits++; + if (rdigits == 0) + return arr; + if (*p) + return arr; + + /* we have a match, so extract start and end */ + start = g_ascii_strtoull(l, NULL, 10); + end = g_ascii_strtoull(r, NULL, 10); + leading_zero = *l == '0'; + ndigits = MAX(ldigits, rdigits); + if (start > end) + return arr; + + /* sanity check.. */ + if (end - start > 100000) + return arr; + + /* remove last from the array */ + g_ptr_array_remove_index(arr, arr->len - 1); + + /* Add new elements */ + while (start <= end) { + if (leading_zero) { + g_ptr_array_add(arr, g_strdup_printf("%0*ju", + ndigits, (uintmax_t)start)); + } else { + g_ptr_array_add(arr, g_strdup_printf("%ju", (uintmax_t)start)); + } + start++; + } + + return arr; +} + +/* Helper for expand_braced_alternates; returns a list of un-escaped strings + * for the first "component" of str, where a component is a plain string or a + * brace-enclosed set of alternatives. str is pointing to the first character + * of the next component on return. */ +static GPtrArray * +parse_braced_component(char **str) +{ + GPtrArray *result = g_ptr_array_new(); + + if (**str == '{') { + char *p = (*str)+1; + char *local = g_malloc(strlen(*str)+1); + char *current = local; + char *c = current; + + while (1) { + if (*p == '\0' || *p == '{') { + /* unterminated { .. } or extra '{' */ + amfree(local); + g_ptr_array_free(result, TRUE); + return NULL; + } + + if (*p == '}' || *p == ',') { + *c = '\0'; + g_ptr_array_add(result, g_strdup(current)); + result = expand_braced_sequence(result); + current = ++c; + + if (*p == '}') + break; + else + p++; + } + + if (*p == '\\') { + if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',') + p++; + } + *(c++) = *(p++); + } + + amfree(local); + + if (*p) + *str = p+1; + else + *str = p; + } else { + /* no braces -- just un-escape a plain string */ + char *local = g_malloc(strlen(*str)+1); + char *r = local; + char *p = *str; + + while (*p && *p != '{') { + if (*p == '\\') { + if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',') + p++; + } + *(r++) = *(p++); + } + *r = '\0'; + g_ptr_array_add(result, local); + *str = p; + } + + return result; +} + +GPtrArray * +expand_braced_alternates( + char * source) +{ + GPtrArray *rval = g_ptr_array_new(); + gpointer *pdata; + + g_ptr_array_add(rval, g_strdup("")); + + while (*source) { + GPtrArray *new_components; + GPtrArray *new_rval; + guint i, j; + + new_components = parse_braced_component(&source); + if (!new_components) { + /* parse error */ + for (i = 0, pdata = rval->pdata; i < rval->len; i++) + g_free(*pdata++); + g_ptr_array_free(rval, TRUE); + return NULL; + } + + new_rval = g_ptr_array_new(); + + /* do a cartesian join of rval and new_components */ + for (i = 0; i < rval->len; i++) { + for (j = 0; j < new_components->len; j++) { + g_ptr_array_add(new_rval, g_strconcat( + g_ptr_array_index(rval, i), + g_ptr_array_index(new_components, j), + NULL)); + } + } + + for (i = 0, pdata = rval->pdata; i < rval->len; i++) + g_free(*pdata++); + g_ptr_array_free(rval, TRUE); + for (i = 0, pdata = new_components->pdata; i < new_components->len; i++) + g_free(*pdata++); + g_ptr_array_free(new_components, TRUE); + rval = new_rval; + } + + return rval; +} + +char * +collapse_braced_alternates( + GPtrArray *source) +{ + GString *result = NULL; + guint i; + + result = g_string_new("{"); + + for (i = 0; i < source->len; i ++) { + const char *str = g_ptr_array_index(source, i); + char *qstr = NULL; + + if (strchr(str, ',') || strchr(str, '\\') || + strchr(str, '{') || strchr(str, '}')) { + const char *s; + char *d; + + s = str; + qstr = d = g_malloc(strlen(str)*2+1); + while (*s) { + if (*s == ',' || *s == '\\' || *s == '{' || *s == '}') + *(d++) = '\\'; + *(d++) = *(s++); + } + *(d++) = '\0'; + } + g_string_append_printf(result, "%s%s", qstr? qstr : str, + (i < source->len-1)? "," : ""); + if (qstr) + g_free(qstr); + } + + g_string_append(result, "}"); + return g_string_free(result, FALSE); +} + /* Return 0 if the following characters are present * ( ) < > [ ] , ; : ! $ \ / " @@ -535,7 +1059,7 @@ int copy_file( { int infd, outfd; int save_errno; - ssize_t nb; + size_t nb; char buf[32768]; char *quoted; @@ -559,7 +1083,7 @@ int copy_file( } while((nb=read(infd, &buf, SIZEOF(buf))) > 0) { - if(fullwrite(outfd,&buf,(size_t)nb) < nb) { + if(full_write(outfd,&buf,nb) < nb) { save_errno = errno; quoted = quote_string(dst); *errmsg = vstrallocf(_("Error writing to '%s': %s"), @@ -571,7 +1095,7 @@ int copy_file( } } - if (nb < 0) { + if (errno != 0) { save_errno = errno; quoted = quote_string(src); *errmsg = vstrallocf(_("Error reading from '%s': %s"), @@ -587,7 +1111,7 @@ int copy_file( return 0; } -#ifndef HAVE_READLINE +#ifndef HAVE_LIBREADLINE /* * simple readline() replacements, used when we don't have readline * support from the system. @@ -622,6 +1146,11 @@ add_history( # error No readdir() or readdir64() available! #endif +#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31)) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + char * portable_readdir(DIR* handle) { #ifdef USE_DIRENT64 @@ -651,6 +1180,9 @@ char * portable_readdir(DIR* handle) { sure what to do about that case. */ return strdup(entry_p->d_name); } +#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31)) +# pragma GCC diagnostic pop +#endif int search_directory(DIR * handle, const char * regex, SearchDirectoryFunctor functor, gpointer user_data) { @@ -712,28 +1244,6 @@ int compare_possibly_null_strings(const char * a, const char * b) { } } -gboolean amanda_thread_init(void) { - gboolean success = FALSE; -#ifdef HAVE_LIBCURL - static gboolean did_curl_init = FALSE; - if (!did_curl_init) { -# ifdef G_THREADS_ENABLED - g_assert(!g_thread_supported()); -# endif - g_assert(curl_global_init(CURL_GLOBAL_ALL) == 0); - did_curl_init = TRUE; - } -#endif -#if defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE) - if (g_thread_supported()) { - return TRUE; - } - g_thread_init(NULL); - success = TRUE; -#endif - return success; -} - int resolve_hostname(const char *hostname, int socktype, @@ -756,7 +1266,14 @@ resolve_hostname(const char *hostname, #endif memset(&hints, 0, sizeof(hints)); +#ifdef WORKING_IPV6 + /* get any kind of addresss */ hints.ai_family = AF_UNSPEC; +#else + /* even if getaddrinfo supports IPv6, don't let it return + * such an address */ + hints.ai_family = AF_INET; +#endif hints.ai_flags = flags; hints.ai_socktype = socktype; result = getaddrinfo(hostname, NULL, &hints, &myres); @@ -844,6 +1361,12 @@ check_running_as(running_as_flags who) #endif switch (who & RUNNING_AS_USER_MASK) { + case RUNNING_AS_ANY: + uid_target = uid_me; + uname_target = uname_me; + amfree(uname_me); + return; + case RUNNING_AS_ROOT: uid_target = 0; uname_target = "root"; @@ -905,12 +1428,39 @@ int set_root_privs(int need_root) { #ifndef SINGLE_USERID - if (need_root) { + static gboolean first_call = TRUE; + static uid_t unpriv = 1; + + if (first_call) { + /* save the original real userid (that of our invoker) */ + unpriv = getuid(); + + /* and set all of our userids (including, importantly, the saved + * userid) to 0 */ + setuid(0); + + /* don't need to do this next time */ + first_call = FALSE; + } + + if (need_root == 1) { + if (geteuid() == 0) return 1; /* already done */ + if (seteuid(0) == -1) return 0; /* (we don't switch the group back) */ + } else if (need_root == -1) { + /* make sure the euid is 0 so that we can set the uid */ + if (geteuid() != 0) { + if (seteuid(0) == -1) return 0; + } + + /* now set the uid to the unprivileged userid */ + if (setuid(unpriv) == -1) return 0; } else { - if (geteuid() != 0) return 0; - if (seteuid(getuid()) == -1) return 0; + if (geteuid() != 0) return 1; /* already done */ + + /* set the *effective* userid only */ + if (seteuid(unpriv) == -1) return 0; if (setegid(getgid()) == -1) return 0; } #else @@ -923,28 +1473,220 @@ int become_root(void) { #ifndef SINGLE_USERID + /* first, set the effective userid to 0 */ + if (seteuid(0) == -1) return 0; + + /* then, set all of the userids to 0 */ if (setuid(0) == -1) return 0; #endif return 1; } +char * +base64_decode_alloc_string( + char *in) +{ + char *out; + size_t in_len = strlen(in); + size_t out_len = 3 * (in_len / 4) + 3; + + out = malloc(out_len); + if (!base64_decode(in, in_len, out, &out_len)) { + amfree(out); + return NULL; + } + out[out_len] = '\0'; + + return out; +} + + +/* A GHFunc (callback for g_hash_table_foreach), + * Store a property and it's value in an ARGV. + * + * @param key_p: (char *) property name. + * @param value_p: (GSList *) property values list. + * @param user_data_p: (char ***) pointer to ARGV. + */ +static void +proplist_add_to_argv( + gpointer key_p, + gpointer value_p, + gpointer user_data_p) +{ + char *property_s = key_p; + property_t *value_s = value_p; + GPtrArray *argv_ptr = user_data_p; + GSList *value; + char *q, *w, *qprop; + + q = stralloc(property_s); + /* convert to lower case */ + for (w=q; *w != '\0'; w++) { + *w = tolower(*w); + if (*w == '_') + *w = '-'; + } + qprop = stralloc2("--", q); + amfree(q); + for(value=value_s->values; value != NULL; value = value->next) { + g_ptr_array_add(argv_ptr, stralloc(qprop)); + g_ptr_array_add(argv_ptr, stralloc((char *)value->data)); + } + amfree(qprop); +} + +void +property_add_to_argv( + GPtrArray *argv_ptr, + GHashTable *proplist) +{ + g_hash_table_foreach(proplist, &proplist_add_to_argv, argv_ptr); +} + + /* * Process parameters */ -/* current process name */ -#define MAX_PNAME 128 -static char pname[MAX_PNAME] = "unknown"; +static char *pname = NULL; +static char *ptype = NULL; +static pcontext_t pcontext = CONTEXT_DEFAULT; void set_pname(char *p) { - g_strlcpy(pname, p, sizeof(pname)); + pname = newstralloc(pname, p); } char * get_pname(void) { + if (!pname) pname = stralloc("unknown"); return pname; } +void +set_ptype(char *p) +{ + ptype = newstralloc(ptype, p); +} + +char * +get_ptype(void) +{ + if (!ptype) ptype = stralloc("unknown"); + return ptype; +} + +void +set_pcontext(pcontext_t pc) +{ + pcontext = pc; +} + +pcontext_t +get_pcontext(void) +{ + return pcontext; +} + +#ifdef __OpenBSD__ +void +openbsd_fd_inform(void) +{ + int i; + for (i = DATA_FD_OFFSET; i < DATA_FD_OFFSET + DATA_FD_COUNT*2; i++) { + /* a simple fcntl() will cause the library to "look" at this file + * descriptor, which is good enough */ + (void)fcntl(i, F_GETFL); + } +} +#endif + +void +debug_executing( + GPtrArray *argv_ptr) +{ + guint i; + char *cmdline = stralloc((char *)g_ptr_array_index(argv_ptr, 0)); + + for (i = 1; i < argv_ptr->len-1; i++) { + char *arg = g_shell_quote((char *)g_ptr_array_index(argv_ptr, i)); + cmdline = vstrextend(&cmdline, " ", arg, NULL); + amfree(arg); + } + g_debug("Executing: %s\n", cmdline); + amfree(cmdline); +} + +char * +get_first_line( + GPtrArray *argv_ptr) +{ + char *output_string = NULL; + int inpipe[2], outpipe[2], errpipe[2]; + int pid; + FILE *out, *err; + + assert(argv_ptr != NULL); + assert(argv_ptr->pdata != NULL); + assert(argv_ptr->len >= 1); + + if (pipe(inpipe) == -1) { + error(_("error [open pipe: %s]"), strerror(errno)); + /*NOTREACHED*/ + } + if (pipe(outpipe) == -1) { + error(_("error [open pipe: %s]"), strerror(errno)); + /*NOTREACHED*/ + } + if (pipe(errpipe) == -1) { + error(_("error [open pipe: %s]"), strerror(errno)); + /*NOTREACHED*/ + } + + fflush(stdout); + switch(pid = fork()) { + case -1: + error(_("error [fork: %s]"), strerror(errno)); + /*NOTREACHED*/ + + default: /* parent process */ + aclose(inpipe[0]); + aclose(outpipe[1]); + aclose(errpipe[1]); + break; + + case 0: /* child process */ + aclose(inpipe[1]); + aclose(outpipe[0]); + aclose(errpipe[0]); + + dup2(inpipe[0], 0); + dup2(outpipe[1], 1); + dup2(errpipe[1], 2); + + debug_executing(argv_ptr); + g_fprintf(stdout, "unknown\n"); + execv((char *)*argv_ptr->pdata, (char **)argv_ptr->pdata); + error(_("error [exec %s: %s]"), (char *)*argv_ptr->pdata, strerror(errno)); + } + + aclose(inpipe[1]); + + out = fdopen(outpipe[0],"r"); + err = fdopen(errpipe[0],"r"); + + output_string = agets(out); + if (!output_string) + output_string = agets(err); + + fclose(out); + fclose(err); + + waitpid(pid, NULL, 0); + + return output_string; +} +