2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1999 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $
35 #include "sockaddr-util.h"
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);
49 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
54 g_debug("make_socket opening socket with family %d", family);
55 s = socket(family, SOCK_STREAM, 0);
58 dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno));
62 if (s < 0 || s >= (int)FD_SETSIZE) {
64 errno = EMFILE; /* out of range */
69 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
72 dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"),
79 r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
80 (void *)&on, SIZEOF(on));
83 dbprintf(_("make_socket: setsockopt() failed: %s\n"),
84 strerror(save_errno));
94 /* addrp is my address */
95 /* svaddr is the address of the remote machine */
96 /* return socket on success */
97 /* return -1 on failure */
100 sockaddr_union *addrp,
101 in_port_t first_port,
104 sockaddr_union *svaddr,
109 static in_port_t port_in_use[1024];
110 static int nb_port_in_use = 0;
112 int save_errno = EAGAIN;
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;
124 if (errno != EAGAIN && errno != EBUSY)
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;
134 port_in_use[nb_port_in_use++] = port;
137 if (errno != EAGAIN && errno != EBUSY)
141 dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
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 */
155 sockaddr_union *addrp,
158 sockaddr_union *svaddr,
162 struct servent * servPort;
164 socklen_t_equiv socklen;
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);
175 if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2;
177 SU_SET_PORT(addrp, port);
178 socklen = SS_LEN(addrp);
179 if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
182 if(servPort == NULL) {
183 dbprintf(_("connect_port: Try port %d: available - %s\n"),
184 port, strerror(errno));
186 dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"),
187 port, servPort->s_name, strerror(errno));
189 if (save_errno != EADDRINUSE) {
197 if(servPort == NULL) {
198 dbprintf(_("connect_port: Try port %d: available - Success\n"), port);
200 dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"),
201 port, servPort->s_name);
204 /* find out what port was actually used */
206 len = sizeof(*addrp);
207 if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
209 dbprintf(_("connect_port: getsockname() failed: %s\n"),
210 strerror(save_errno));
217 fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
218 if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
220 dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
222 strerror(save_errno));
223 dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
224 str_sockaddr(svaddr),
225 strerror(save_errno));
228 if (save_errno == ECONNREFUSED ||
229 save_errno == EHOSTUNREACH ||
230 save_errno == ENETUNREACH ||
231 save_errno == ETIMEDOUT) {
237 dbprintf(_("connected to %s\n"),
238 str_sockaddr(svaddr));
239 dbprintf(_("our side is %s\n"),
240 str_sockaddr(addrp));
246 * Bind to a port in the given range. Takes a begin,end pair of port numbers.
248 * Returns negative on error (EGAIN if all ports are in use). Returns 0
254 sockaddr_union *addrp,
255 in_port_t first_port,
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;
266 assert(first_port <= last_port);
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.
272 port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
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.
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);
288 dbprintf(_("bind_portrange2: Try port %d: Owned by %s - Success.\n"), port, servPort->s_name);
292 if (errno != EAGAIN && errno != EBUSY)
294 if (servPort == NULL) {
295 dbprintf(_("bind_portrange2: Try port %d: Available - %s\n"),
296 port, strerror(errno));
298 dbprintf(_("bind_portrange2: Try port %d: Owned by %s - %s\n"),
299 port, servPort->s_name, strerror(errno));
302 dbprintf(_("bind_portrange2: Skip port %d: Owned by %s.\n"),
303 port, servPort->s_name);
305 if (++port > last_port)
308 dbprintf(_("bind_portrange: all ports between %d and %d busy\n"),
316 * Writes out the entire iovec
324 ssize_t delta, n, total;
333 n = writev(fd, iov, iovcnt);
344 * Iterate through each iov. Figure out what we still need
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 */
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)
370 return (match("[ \t\f\r\n\"]", str) != 0);
375 * For backward compatibility we are trying for minimal quoting.
376 * We only quote a string if it contains whitespace or is misquoted...
386 if ((str == NULL) || (*str == '\0')) {
387 ret = stralloc("\"\"");
388 } else if ((match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) {
390 * String does not need to be quoted since it contains
391 * neither whitespace, control or quote characters.
396 * Allocate maximum possible string length.
397 * (a string of all quotes plus room for leading ", trailing " and NULL)
399 ret = s = alloc((strlen(str) * 2) + 2 + 1);
401 while (*str != '\0') {
407 } else if (*str == '\n') {
412 } else if (*str == '\r') {
417 } else if (*str == '\f') {
422 } else if (*str == '\\') {
445 if ((str == NULL) || (*str == '\0')) {
451 ret = in = out = stralloc(str);
452 while (*in != '\0') {
464 } else if (*in == 't') {
468 } else if (*in == 'r') {
472 } else if (*in == 'f') {
476 } else if (*in >= '0' && *in <= '7') {
480 while (i < 3 && *in >= '0' && *in <= '7') {
481 c = (c << 3) + *(in++) - '0';
486 } else if (*in == '\0') {
487 /* trailing backslash -- ignore */
499 split_quoted_strings(
502 char *local = g_strdup(string);
506 GPtrArray *strs = g_ptr_array_new();
510 if (!iq && *p == ' ') {
512 g_ptr_array_add(strs, unquote_string(start));
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 */
519 } else if (*p == '\"') {
526 g_ptr_array_add(strs, unquote_string(start));
528 /* now convert strs into a strv, by stealing its references to the underlying
530 result = g_new0(char *, strs->len + 1);
531 memmove(result, strs->pdata, sizeof(char *) * strs->len);
533 g_ptr_array_free(strs, FALSE); /* FALSE => don't free strings */
540 strquotedstr(char **saveptr)
542 char * tok = strtok_r(NULL, " ", saveptr);
554 while (in_quote || in_backslash || *p != '\0') {
556 /* append a new token */
557 t = strtok_r(NULL, " ", saveptr);
565 in_quote = !in_quote;
566 else if (*p == '\\') {
584 if ((str == NULL) || (*str == '\0')) {
588 for (s = ret; *s != '\0'; s++) {
589 if (iscntrl((int)*s))
597 Return 0 if the following characters are present
598 * ( ) < > [ ] , ; : ! $ \ / "
606 return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
620 if ((infd = open(src, O_RDONLY)) == -1) {
622 quoted = quote_string(src);
623 *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
624 quoted, strerror(save_errno));
629 if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
631 quoted = quote_string(dst);
632 *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
633 quoted, strerror(save_errno));
639 while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
640 if(full_write(outfd,&buf,nb) < nb) {
642 quoted = quote_string(dst);
643 *errmsg = vstrallocf(_("Error writing to '%s': %s"),
644 quoted, strerror(save_errno));
654 quoted = quote_string(src);
655 *errmsg = vstrallocf(_("Error reading from '%s': %s"),
656 quoted, strerror(save_errno));
668 #ifndef HAVE_READLINE
670 * simple readline() replacements, used when we don't have readline
671 * support from the system.
678 g_printf("%s", prompt);
688 (void)line; /* Quiet unused parameter warning */
693 /* Order of preference: readdir64(), readdir(). */
694 #if HAVE_DECL_READDIR64
695 # define USE_DIRENT64
696 # define USE_READDIR64
697 #elif HAVE_DECL_READDIR
700 # error No readdir() or readdir64() available!
703 char * portable_readdir(DIR* handle) {
706 struct dirent64 *entry_p;
708 struct dirent *entry_p;
711 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
713 g_static_mutex_lock(&mutex);
716 entry_p = readdir(handle);
719 entry_p = readdir64(handle);
722 g_static_mutex_unlock(&mutex);
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);
733 int search_directory(DIR * handle, const char * regex,
734 SearchDirectoryFunctor functor, gpointer user_data) {
736 regex_t compiled_regex;
737 gboolean done = FALSE;
739 if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
740 regfree(&compiled_regex);
749 read_name = portable_readdir(handle);
750 if (read_name == NULL) {
751 regfree(&compiled_regex);
754 result = regexec(&compiled_regex, read_name, 0, NULL, 0);
757 done = !functor(read_name, user_data);
761 regfree(&compiled_regex);
765 char* find_regex_substring(const char* base_string, const regmatch_t match) {
769 size = match.rm_eo - match.rm_so;
770 rval = malloc(size+1);
771 memcpy(rval, base_string + match.rm_so, size);
777 int compare_possibly_null_strings(const char * a, const char * b) {
779 /* NULL or otherwise, they're the same. */
781 } else if (a == NULL) {
784 } else if (b == NULL) {
794 resolve_hostname(const char *hostname,
796 struct addrinfo **res,
799 struct addrinfo hints;
800 struct addrinfo *myres;
804 if (res) *res = NULL;
807 flags = AI_CANONNAME;
811 flags |= AI_ADDRCONFIG;
814 memset(&hints, 0, sizeof(hints));
816 /* get any kind of addresss */
817 hints.ai_family = AF_UNSPEC;
819 /* even if getaddrinfo supports IPv6, don't let it return
821 hints.ai_family = AF_INET;
823 hints.ai_flags = flags;
824 hints.ai_socktype = socktype;
825 result = getaddrinfo(hostname, NULL, &hints, &myres);
830 if (canonname && myres && myres->ai_canonname) {
831 *canonname = stralloc(myres->ai_canonname);
848 if (WIFEXITED(status)) {
849 int exitstatus = WEXITSTATUS(status);
851 return vstrallocf(_("%s exited normally"), subject);
853 return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
856 if (WIFSIGNALED(status)) {
857 int signal = WTERMSIG(status);
859 if (WCOREDUMP(status))
860 return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
864 return vstrallocf(_("%s exited after receiving signal %d"),
868 if (WIFSTOPPED(status)) {
869 int signal = WSTOPSIG(status);
870 return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
875 if (WIFCONTINUED(status)) {
876 return vstrallocf(_("%s was resumed"), subject);
880 return vstrallocf(_("%s exited in unknown circumstances"), subject);
884 check_running_as(running_as_flags who)
890 char *uname_me = NULL;
891 char *uname_target = NULL;
895 if ((pw = getpwuid(uid_me)) == NULL) {
896 error(_("current userid %ld not found in password database"), (long)uid_me);
899 uname_me = stralloc(pw->pw_name);
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);
909 switch (who & RUNNING_AS_USER_MASK) {
912 uname_target = uname_me;
915 case RUNNING_AS_ROOT:
917 uname_target = "root";
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"
930 CLIENT_LOGIN, dumpuser);
931 uid_target = uid_me; /* force success below */
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);
943 uid_target = pw->pw_uid;
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);
952 uid_target = pw->pw_uid;
956 error(_("Unknown check_running_as() call"));
960 if (uid_me != uid_target) {
961 error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
967 /* Quiet unused variable warning */
973 set_root_privs(int need_root)
975 #ifndef SINGLE_USERID
977 if (seteuid(0) == -1) return 0;
978 /* (we don't switch the group back) */
980 if (geteuid() != 0) return 0;
981 if (seteuid(getuid()) == -1) return 0;
982 if (setegid(getgid()) == -1) return 0;
985 (void)need_root; /* Quiet unused variable warning */
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;
1004 base64_decode_alloc_string(
1008 size_t in_len = strlen(in);
1009 size_t out_len = 3 * (in_len / 4) + 3;
1011 out = malloc(out_len);
1012 if (!base64_decode(in, in_len, out, &out_len)) {
1016 out[out_len] = '\0';
1022 /* A GHFunc (callback for g_hash_table_foreach) */
1023 void count_proplist(
1024 gpointer key_p G_GNUC_UNUSED,
1026 gpointer user_data_p)
1028 property_t *value_s = value_p;
1029 int *nb = user_data_p;
1032 for(value=value_s->values; value != NULL; value = value->next) {
1037 /* A GHFunc (callback for g_hash_table_foreach) */
1038 void proplist_add_to_argv(
1041 gpointer user_data_p)
1043 char *property_s = key_p;
1044 property_t *value_s = value_p;
1045 char ***argv = user_data_p;
1047 char *q, *w, *qprop;
1049 q = stralloc(property_s);
1050 /* convert to lower case */
1051 for (w=q; *w != '\0'; w++) {
1056 qprop = stralloc2("--", q);
1058 for(value=value_s->values; value != NULL; value = value->next) {
1059 **argv = stralloc(qprop);
1061 **argv = stralloc((char *)value->data);
1069 * Process parameters
1072 static char *pname = NULL;
1073 static char *ptype = NULL;
1074 static pcontext_t pcontext = CONTEXT_DEFAULT;
1079 pname = newstralloc(pname, p);
1085 if (!pname) pname = stralloc("unknown");
1092 ptype = newstralloc(ptype, p);
1098 if (!ptype) ptype = stralloc("unknown");
1103 set_pcontext(pcontext_t pc)