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.
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.
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.
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $
37 #include "sockaddr-util.h"
41 #include "pipespawn.h"
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);
55 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
60 g_debug("make_socket opening socket with family %d", family);
61 s = socket(family, SOCK_STREAM, 0);
64 dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno));
68 if (s < 0 || s >= (int)FD_SETSIZE) {
70 errno = EMFILE; /* out of range */
75 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
78 dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"),
85 r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
86 (void *)&on, SIZEOF(on));
89 dbprintf(_("make_socket: setsockopt() failed: %s\n"),
90 strerror(save_errno));
100 GQuark am_util_error_quark(void)
102 return g_quark_from_static_string("am-util-error-quark");
105 /* addrp is my address */
106 /* svaddr is the address of the remote machine */
107 /* return socket on success */
108 /* return -1 on failure */
111 sockaddr_union *addrp,
112 in_port_t first_port,
115 sockaddr_union *svaddr,
120 static in_port_t port_in_use[1024];
121 static int nb_port_in_use = 0;
123 int save_errno = EAGAIN;
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;
135 if (errno != EAGAIN && errno != EBUSY)
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;
145 port_in_use[nb_port_in_use++] = port;
148 if (errno != EAGAIN && errno != EBUSY)
152 dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
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 */
166 sockaddr_union *addrp,
169 sockaddr_union *svaddr,
173 struct servent * servPort;
175 socklen_t_equiv socklen;
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);
186 if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2;
188 SU_SET_PORT(addrp, port);
189 socklen = SS_LEN(addrp);
190 if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
193 if(servPort == NULL) {
194 dbprintf(_("connect_port: Try port %d: available - %s\n"),
195 port, strerror(errno));
197 dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"),
198 port, servPort->s_name, strerror(errno));
200 if (save_errno != EADDRINUSE) {
208 if(servPort == NULL) {
209 dbprintf(_("connect_port: Try port %d: available - Success\n"), port);
211 dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"),
212 port, servPort->s_name);
215 /* find out what port was actually used */
217 len = sizeof(*addrp);
218 if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
220 dbprintf(_("connect_port: getsockname() failed: %s\n"),
221 strerror(save_errno));
228 fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
229 if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
231 dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
233 strerror(save_errno));
234 dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
235 str_sockaddr(svaddr),
236 strerror(save_errno));
239 if (save_errno == ECONNREFUSED ||
240 save_errno == EHOSTUNREACH ||
241 save_errno == ENETUNREACH ||
242 save_errno == ETIMEDOUT) {
248 dbprintf(_("connected to %s\n"),
249 str_sockaddr(svaddr));
250 dbprintf(_("our side is %s\n"),
251 str_sockaddr(addrp));
257 * Bind to a port in the given range. Takes a begin,end pair of port numbers.
259 * Returns negative on error (EGAIN if all ports are in use). Returns 0
265 sockaddr_union *addrp,
266 in_port_t first_port,
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;
277 assert(first_port <= last_port);
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.
283 port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
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.
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);
299 g_debug(_("bind_portrange2: Try port %d: Owned by %s - Success."), port, servPort->s_name);
303 if (errno != EAGAIN && errno != EBUSY)
305 if (servPort == NULL) {
306 g_debug(_("bind_portrange2: Try port %d: Available - %s"),
307 port, strerror(errno));
309 g_debug(_("bind_portrange2: Try port %d: Owned by %s - %s"),
310 port, servPort->s_name, strerror(errno));
313 g_debug(_("bind_portrange2: Skip port %d: Owned by %s."),
314 port, servPort->s_name);
316 if (++port > last_port)
319 g_debug(_("bind_portrange: all ports between %d and %d busy"),
327 interruptible_accept(
329 struct sockaddr *addr,
331 gboolean (*prolong)(gpointer data),
332 gpointer prolong_data)
334 SELECT_ARG_TYPE readset;
338 if (sock < 0 || sock >= FD_SETSIZE) {
339 g_debug("interruptible_accept: bad socket %d", sock);
343 memset(&readset, 0, SIZEOF(readset));
346 if (!prolong(prolong_data)) {
352 FD_SET(sock, &readset);
354 /* try accepting for 1s */
355 memset(&tv, 0, SIZEOF(tv));
358 nfound = select(sock+1, &readset, NULL, NULL, &tv);
361 } else if (nfound == 0) {
363 } else if (!FD_ISSET(sock, &readset)) {
364 g_debug("interruptible_accept: select malfunction");
368 int rv = accept(sock, addr, addrlen);
369 if (rv < 0 && errno == EAGAIN)
377 * Writes out the entire iovec
385 ssize_t delta, n, total;
394 n = writev(fd, iov, iovcnt);
405 * Iterate through each iov. Figure out what we still need
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 */
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)
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...
440 if ((str == NULL) || (*str == '\0')) {
441 ret = stralloc("\"\"");
444 for (r = str; *r; r++) {
445 if (*r == ':' || *r == '\'' || *r == '\\' || *r == '\"' ||
446 *r <= ' ' || *r == 0x7F )
451 * String does not need to be quoted since it contains
452 * neither whitespace, control or quote characters.
457 * Allocate maximum possible string length.
458 * (a string of all quotes plus room for leading ", trailing " and
461 ret = s = alloc((strlen(str) * 2) + 2 + 1);
463 while (*str != '\0') {
469 } else if (*str == '\n') {
474 } else if (*str == '\r') {
479 } else if (*str == '\f') {
484 } else if (*str == '\\') {
503 len_quote_string_maybe(
509 if ((str == NULL) || (*str == '\0')) {
513 for (r = str; *r; r++) {
514 if (*r == ':' || *r == '\'' || *r == '\\' || *r == '\"' ||
515 *r <= ' ' || *r == 0x7F )
520 * String does not need to be quoted since it contains
521 * neither whitespace, control or quote characters.
526 * Allocate maximum possible string length.
527 * (a string of all quotes plus room for leading ", trailing " and
531 while (*str != '\0') {
537 } else if (*str == '\n') {
542 } else if (*str == '\r') {
547 } else if (*str == '\f') {
552 } else if (*str == '\\') {
576 if ((str == NULL) || (*str == '\0')) {
582 ret = in = out = stralloc(str);
583 while (*in != '\0') {
595 } else if (*in == 't') {
599 } else if (*in == 'r') {
603 } else if (*in == 'f') {
607 } else if (*in >= '0' && *in <= '7') {
611 while (i < 3 && *in >= '0' && *in <= '7') {
612 c = (c << 3) + *(in++) - '0';
617 } else if (*in == '\0') {
618 /* trailing backslash -- ignore */
630 split_quoted_strings(
643 p = start = local = g_strdup(string);
644 strs = g_ptr_array_new();
647 if (!iq && *p == ' ') {
649 g_ptr_array_add(strs, unquote_string(start));
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 */
656 } else if (*p == '\"') {
663 g_ptr_array_add(strs, unquote_string(start));
665 /* now convert strs into a strv, by stealing its references to the underlying
667 result = g_new0(char *, strs->len + 1);
668 memmove(result, strs->pdata, sizeof(char *) * strs->len);
670 g_ptr_array_free(strs, TRUE); /* TRUE => free pdata, strings are not freed */
677 strquotedstr(char **saveptr)
679 char * tok = strtok_r(NULL, " ", saveptr);
691 while (in_quote || in_backslash || *p != '\0') {
693 /* append a new token */
694 t = strtok_r(NULL, " ", saveptr);
702 in_quote = !in_quote;
703 else if (*p == '\\') {
721 if ((str == NULL) || (*str == '\0')) {
725 for (s = ret; *s != '\0'; s++) {
726 if (iscntrl((int)*s))
733 char *hexencode_string(const char *str)
735 size_t orig_len, new_len, i;
739 s = g_string_sized_new(0);
742 new_len = orig_len = strlen(str);
743 for (i = 0; i < orig_len; i++) {
744 if (!g_ascii_isalnum(str[i])) {
748 s = g_string_sized_new(new_len);
750 for (i = 0; i < orig_len; i++) {
751 if (g_ascii_isalnum(str[i])) {
752 g_string_append_c(s, str[i]);
754 g_string_append_printf(s, "%%%02hhx", str[i]);
760 g_string_free(s, FALSE);
764 char *hexdecode_string(const char *str, GError **err)
766 size_t orig_len, new_len, i;
770 s = g_string_sized_new(0);
773 new_len = orig_len = strlen(str);
774 for (i = 0; i < orig_len; i++) {
779 s = g_string_sized_new(new_len);
781 for (i = 0; (orig_len > 2) && (i < orig_len-2); i++) {
785 for (j = 1; j < 3; j++) {
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;
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);
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);
807 g_string_append_c(s, tmp);
810 g_string_append_c(s, str[i]);
813 for ( /*nothing*/; i < orig_len; 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);
820 g_string_append_c(s, str[i]);
826 g_string_free(s, FALSE);
830 /* Helper for parse_braced_component; this will turn a single element array
831 * matching /^\d+\.\.\d+$/ into a sequence of numbered array elements. */
833 expand_braced_sequence(GPtrArray *arr)
837 int ldigits, rdigits, ndigits;
839 gboolean leading_zero;
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);
845 for (l = p = elt; *p && g_ascii_isdigit(*p); p++)
854 for (r = p; *p && g_ascii_isdigit(*p); p++)
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);
870 if (end - start > 100000)
873 /* remove last from the array */
874 g_ptr_array_remove_index(arr, arr->len - 1);
876 /* Add new elements */
877 while (start <= end) {
879 g_ptr_array_add(arr, g_strdup_printf("%0*ju",
880 ndigits, (uintmax_t)start));
882 g_ptr_array_add(arr, g_strdup_printf("%ju", (uintmax_t)start));
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. */
895 parse_braced_component(char **str)
897 GPtrArray *result = g_ptr_array_new();
901 char *local = g_malloc(strlen(*str)+1);
902 char *current = local;
906 if (*p == '\0' || *p == '{') {
907 /* unterminated { .. } or extra '{' */
909 g_ptr_array_free(result, TRUE);
913 if (*p == '}' || *p == ',') {
915 g_ptr_array_add(result, g_strdup(current));
916 result = expand_braced_sequence(result);
926 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
939 /* no braces -- just un-escape a plain string */
940 char *local = g_malloc(strlen(*str)+1);
944 while (*p && *p != '{') {
946 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
952 g_ptr_array_add(result, local);
960 expand_braced_alternates(
963 GPtrArray *rval = g_ptr_array_new();
966 g_ptr_array_add(rval, g_strdup(""));
969 GPtrArray *new_components;
973 new_components = parse_braced_component(&source);
974 if (!new_components) {
976 for (i = 0, pdata = rval->pdata; i < rval->len; i++)
978 g_ptr_array_free(rval, TRUE);
982 new_rval = g_ptr_array_new();
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),
994 for (i = 0, pdata = rval->pdata; i < rval->len; i++)
996 g_ptr_array_free(rval, TRUE);
997 for (i = 0, pdata = new_components->pdata; i < new_components->len; i++)
999 g_ptr_array_free(new_components, TRUE);
1007 collapse_braced_alternates(
1010 GString *result = NULL;
1013 result = g_string_new("{");
1015 for (i = 0; i < source->len; i ++) {
1016 const char *str = g_ptr_array_index(source, i);
1019 if (strchr(str, ',') || strchr(str, '\\') ||
1020 strchr(str, '{') || strchr(str, '}')) {
1025 qstr = d = g_malloc(strlen(str)*2+1);
1027 if (*s == ',' || *s == '\\' || *s == '{' || *s == '}')
1033 g_string_append_printf(result, "%s%s", qstr? qstr : str,
1034 (i < source->len-1)? "," : "");
1039 g_string_append(result, "}");
1040 return g_string_free(result, FALSE);
1044 Return 0 if the following characters are present
1045 * ( ) < > [ ] , ; : ! $ \ / "
1053 return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
1067 if ((infd = open(src, O_RDONLY)) == -1) {
1069 quoted = quote_string(src);
1070 *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
1071 quoted, strerror(save_errno));
1076 if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
1078 quoted = quote_string(dst);
1079 *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
1080 quoted, strerror(save_errno));
1086 while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
1087 if(full_write(outfd,&buf,nb) < (size_t)nb) {
1089 quoted = quote_string(dst);
1090 *errmsg = vstrallocf(_("Error writing to '%s': %s"),
1091 quoted, strerror(save_errno));
1101 quoted = quote_string(src);
1102 *errmsg = vstrallocf(_("Error reading from '%s': %s"),
1103 quoted, strerror(save_errno));
1115 #ifndef HAVE_LIBREADLINE
1117 * simple readline() replacements, used when we don't have readline
1118 * support from the system.
1125 g_printf("%s", prompt);
1128 return agets(stdin);
1135 (void)line; /* Quiet unused parameter warning */
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
1147 # error No readdir() or readdir64() available!
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"
1155 char * portable_readdir(DIR* handle) {
1158 struct dirent64 *entry_p;
1160 struct dirent *entry_p;
1163 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1165 g_static_mutex_lock(&mutex);
1168 entry_p = readdir(handle);
1170 #ifdef USE_READDIR64
1171 entry_p = readdir64(handle);
1174 g_static_mutex_unlock(&mutex);
1176 if (entry_p == NULL)
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);
1184 #if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
1185 # pragma GCC diagnostic pop
1188 int search_directory(DIR * handle, const char * regex,
1189 SearchDirectoryFunctor functor, gpointer user_data) {
1191 regex_t compiled_regex;
1192 gboolean done = FALSE;
1194 if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
1195 regfree(&compiled_regex);
1204 read_name = portable_readdir(handle);
1205 if (read_name == NULL) {
1206 regfree(&compiled_regex);
1209 result = regexec(&compiled_regex, read_name, 0, NULL, 0);
1212 done = !functor(read_name, user_data);
1216 regfree(&compiled_regex);
1220 char* find_regex_substring(const char* base_string, const regmatch_t match) {
1224 size = match.rm_eo - match.rm_so;
1225 rval = malloc(size+1);
1226 memcpy(rval, base_string + match.rm_so, size);
1232 int compare_possibly_null_strings(const char * a, const char * b) {
1234 /* NULL or otherwise, they're the same. */
1236 } else if (a == NULL) {
1239 } else if (b == NULL) {
1243 /* a != NULL != b */
1244 return strcmp(a, b);
1249 resolve_hostname(const char *hostname,
1251 struct addrinfo **res,
1254 struct addrinfo hints;
1255 struct addrinfo *myres;
1259 if (res) *res = NULL;
1262 flags = AI_CANONNAME;
1265 #ifdef AI_ADDRCONFIG
1266 flags |= AI_ADDRCONFIG;
1269 memset(&hints, 0, sizeof(hints));
1271 /* get any kind of addresss */
1272 hints.ai_family = AF_UNSPEC;
1274 /* even if getaddrinfo supports IPv6, don't let it return
1275 * such an address */
1276 hints.ai_family = AF_INET;
1278 hints.ai_flags = flags;
1279 hints.ai_socktype = socktype;
1280 result = getaddrinfo(hostname, NULL, &hints, &myres);
1285 if (canonname && myres && myres->ai_canonname) {
1286 *canonname = stralloc(myres->ai_canonname);
1292 freeaddrinfo(myres);
1303 if (WIFEXITED(status)) {
1304 int exitstatus = WEXITSTATUS(status);
1305 if (exitstatus == 0)
1306 return vstrallocf(_("%s exited normally"), subject);
1308 return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
1311 if (WIFSIGNALED(status)) {
1312 int signal = WTERMSIG(status);
1314 if (WCOREDUMP(status))
1315 return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
1319 return vstrallocf(_("%s exited after receiving signal %d"),
1323 if (WIFSTOPPED(status)) {
1324 int signal = WSTOPSIG(status);
1325 return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
1330 if (WIFCONTINUED(status)) {
1331 return vstrallocf(_("%s was resumed"), subject);
1335 return vstrallocf(_("%s exited in unknown circumstances"), subject);
1339 check_running_as(running_as_flags who)
1345 char *uname_me = NULL;
1346 char *uname_target = NULL;
1350 if ((pw = getpwuid(uid_me)) == NULL) {
1351 error(_("current userid %ld not found in password database"), (long)uid_me);
1354 uname_me = stralloc(pw->pw_name);
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);
1364 switch (who & RUNNING_AS_USER_MASK) {
1365 case RUNNING_AS_ANY:
1366 uid_target = uid_me;
1367 uname_target = uname_me;
1371 case RUNNING_AS_ROOT:
1373 uname_target = "root";
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"
1386 CLIENT_LOGIN, dumpuser);
1387 uid_target = uid_me; /* force success below */
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);
1399 uid_target = pw->pw_uid;
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);
1408 uid_target = pw->pw_uid;
1412 error(_("Unknown check_running_as() call"));
1416 if (uid_me != uid_target) {
1417 error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
1423 /* Quiet unused variable warning */
1429 set_root_privs(int need_root)
1431 #ifndef SINGLE_USERID
1432 static gboolean first_call = TRUE;
1433 static uid_t unpriv = 1;
1436 /* save the original real userid (that of our invoker) */
1439 /* and set all of our userids (including, importantly, the saved
1443 /* don't need to do this next time */
1447 if (need_root == 1) {
1448 if (geteuid() == 0) return 1; /* already done */
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;
1458 /* now set the uid to the unprivileged userid */
1459 if (setuid(unpriv) == -1) return 0;
1461 if (geteuid() != 0) return 1; /* already done */
1463 /* set the *effective* userid only */
1464 if (seteuid(unpriv) == -1) return 0;
1465 if (setegid(getgid()) == -1) return 0;
1468 (void)need_root; /* Quiet unused variable warning */
1476 #ifndef SINGLE_USERID
1477 /* first, set the effective userid to 0 */
1478 if (seteuid(0) == -1) return 0;
1480 /* then, set all of the userids to 0 */
1481 if (setuid(0) == -1) return 0;
1487 base64_decode_alloc_string(
1491 size_t in_len = strlen(in);
1492 size_t out_len = 3 * (in_len / 4) + 3;
1494 out = malloc(out_len);
1495 if (!base64_decode(in, in_len, out, &out_len)) {
1499 out[out_len] = '\0';
1505 /* A GHFunc (callback for g_hash_table_foreach),
1506 * Store a property and it's value in an ARGV.
1508 * @param key_p: (char *) property name.
1509 * @param value_p: (GSList *) property values list.
1510 * @param user_data_p: (char ***) pointer to ARGV.
1513 proplist_add_to_argv(
1516 gpointer user_data_p)
1518 char *property_s = key_p;
1519 property_t *value_s = value_p;
1520 GPtrArray *argv_ptr = user_data_p;
1522 char *q, *w, *qprop;
1524 q = stralloc(property_s);
1525 /* convert to lower case */
1526 for (w=q; *w != '\0'; w++) {
1531 qprop = stralloc2("--", 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));
1541 property_add_to_argv(
1542 GPtrArray *argv_ptr,
1543 GHashTable *proplist)
1545 g_hash_table_foreach(proplist, &proplist_add_to_argv, argv_ptr);
1550 * Process parameters
1553 static char *pname = NULL;
1554 static char *ptype = NULL;
1555 static pcontext_t pcontext = CONTEXT_DEFAULT;
1560 pname = newstralloc(pname, p);
1566 if (!pname) pname = stralloc("unknown");
1573 ptype = newstralloc(ptype, p);
1579 if (!ptype) ptype = stralloc("unknown");
1584 set_pcontext(pcontext_t pc)
1597 openbsd_fd_inform(void)
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);
1610 GPtrArray *argv_ptr)
1613 char *cmdline = stralloc((char *)g_ptr_array_index(argv_ptr, 0));
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);
1620 g_debug("Executing: %s\n", cmdline);
1626 GPtrArray *argv_ptr)
1628 char *output_string = NULL;
1629 int inpipe[2], outpipe[2], errpipe[2];
1633 assert(argv_ptr != NULL);
1634 assert(argv_ptr->pdata != NULL);
1635 assert(argv_ptr->len >= 1);
1637 if (pipe(inpipe) == -1) {
1638 error(_("error [open pipe: %s]"), strerror(errno));
1641 if (pipe(outpipe) == -1) {
1642 error(_("error [open pipe: %s]"), strerror(errno));
1645 if (pipe(errpipe) == -1) {
1646 error(_("error [open pipe: %s]"), strerror(errno));
1651 switch(pid = fork()) {
1653 error(_("error [fork: %s]"), strerror(errno));
1656 default: /* parent process */
1662 case 0: /* child process */
1668 dup2(outpipe[1], 1);
1669 dup2(errpipe[1], 2);
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));
1679 out = fdopen(outpipe[0],"r");
1680 err = fdopen(errpipe[0],"r");
1683 output_string = agets(out);
1689 output_string = agets(err);
1693 waitpid(pid, NULL, 0);
1695 return output_string;
1699 make_amanda_tmpdir(void)
1701 struct stat stat_buf;
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));
1709 if (mkdir(AMANDA_TMPDIR,0700) != 0) {
1710 g_debug("Error mkdir of AMANDA_TMPDIR (%s): %s", AMANDA_TMPDIR, strerror(errno));
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));
1719 /* check permission */