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 #include <curl/curl.h>
42 static int make_socket(sa_family_t family);
43 static int connect_port(struct sockaddr_storage *addrp, in_port_t port, char *proto,
44 struct sockaddr_storage *svaddr, int nonblock);
47 * Keep calling read() until we've read buflen's worth of data, or EOF,
50 * Returns the number of bytes read, 0 on EOF, or negative on error.
58 ssize_t nread, tot = 0;
59 char *buf = vbuf; /* cast to char so we can ++ it */
62 nread = read(fd, buf, buflen);
64 if ((errno == EINTR) || (errno == EAGAIN))
66 return ((tot > 0) ? tot : -1);
80 * Keep calling write() until we've written buflen's worth of data,
83 * Returns the number of bytes written, or negative on error.
91 ssize_t nwritten, tot = 0;
92 const char *buf = vbuf; /* cast to char so we can ++ it */
95 nwritten = write(fd, buf, buflen);
97 if ((errno == EINTR) || (errno == EAGAIN))
99 return ((tot > 0) ? tot : -1);
114 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
119 s = socket(family, SOCK_STREAM, 0);
122 dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno));
126 if (s < 0 || s >= (int)FD_SETSIZE) {
128 errno = EMFILE; /* out of range */
133 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
136 dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"),
143 r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
144 (void *)&on, SIZEOF(on));
147 dbprintf(_("make_socket: setsockopt() failed: %s\n"),
148 strerror(save_errno));
158 /* addrp is my address */
159 /* svaddr is the address of the remote machine */
160 /* return socket on success */
161 /* return -1 on failure */
164 struct sockaddr_storage *addrp,
165 in_port_t first_port,
168 struct sockaddr_storage *svaddr,
173 static in_port_t port_in_use[1024];
174 static int nb_port_in_use = 0;
177 assert(first_port <= last_port);
178 /* Try a port already used */
179 for(i=0; i < nb_port_in_use; i++) {
180 port = port_in_use[i];
181 if(port >= first_port && port <= last_port) {
182 s = connect_port(addrp, port, proto, svaddr, nonblock);
183 if(s == -2) return -1;
190 /* Try a port in the range */
191 for (port = first_port; port <= last_port; port++) {
192 s = connect_port(addrp, port, proto, svaddr, nonblock);
193 if(s == -2) return -1;
195 port_in_use[nb_port_in_use++] = port;
200 dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
207 /* addrp is my address */
208 /* svaddr is the address of the remote machine */
209 /* return -2: Don't try again */
210 /* return -1: Try with another port */
211 /* return >0: this is the connected socket */
214 struct sockaddr_storage *addrp,
217 struct sockaddr_storage *svaddr,
221 struct servent * servPort;
226 servPort = getservbyport((int)htons(port), proto);
227 if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
228 dbprintf(_("connect_port: Skip port %d: owned by %s.\n"),
229 port, servPort->s_name);
233 if ((s = make_socket(addrp->ss_family)) == -1) return -2;
235 SS_SET_PORT(addrp, port);
236 socklen = SS_LEN(addrp);
237 if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
240 if(servPort == NULL) {
241 dbprintf(_("connect_port: Try port %d: available - %s\n"),
242 port, strerror(errno));
244 dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"),
245 port, servPort->s_name, strerror(errno));
247 if (save_errno != EADDRINUSE) {
255 if(servPort == NULL) {
256 dbprintf(_("connect_port: Try port %d: available - Success\n"), port);
258 dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"),
259 port, servPort->s_name);
262 /* find out what port was actually used */
264 len = sizeof(*addrp);
265 if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
267 dbprintf(_("connect_port: getsockname() failed: %s\n"),
268 strerror(save_errno));
275 fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
276 if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
278 dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
280 strerror(save_errno));
281 dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
282 str_sockaddr(svaddr),
283 strerror(save_errno));
286 if (save_errno == ECONNREFUSED ||
287 save_errno == EHOSTUNREACH ||
288 save_errno == ENETUNREACH ||
289 save_errno == ETIMEDOUT) {
295 dbprintf(_("connected to %s\n"),
296 str_sockaddr(svaddr));
297 dbprintf(_("our side is %s\n"),
298 str_sockaddr(addrp));
304 * Bind to a port in the given range. Takes a begin,end pair of port numbers.
306 * Returns negative on error (EGAIN if all ports are in use). Returns 0
312 struct sockaddr_storage *addrp,
313 in_port_t first_port,
320 struct servent *servPort;
321 const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
323 assert(first_port <= last_port);
326 * We pick a different starting port based on our pid and the current
327 * time to avoid always picking the same reserved port twice.
329 port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
332 * Scan through the range, trying all available ports that are either
333 * not taken in /etc/services or registered for *amanda*. Wrap around
334 * if we don't happen to start at the beginning.
336 for (cnt = 0; cnt < num_ports; cnt++) {
337 servPort = getservbyport((int)htons(port), proto);
338 if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
339 SS_SET_PORT(addrp, port);
340 socklen = SS_LEN(addrp);
341 if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) {
342 if (servPort == NULL) {
343 dbprintf(_("bind_portrange2: Try port %d: Available - Success\n"), port);
345 dbprintf(_("bind_portrange2: Try port %d: Owned by %s - Success.\n"), port, servPort->s_name);
349 if (servPort == NULL) {
350 dbprintf(_("bind_portrange2: Try port %d: Available - %s\n"),
351 port, strerror(errno));
353 dbprintf(_("bind_portrange2: Try port %d: Owned by %s - %s\n"),
354 port, servPort->s_name, strerror(errno));
357 dbprintf(_("bind_portrange2: Skip port %d: Owned by %s.\n"),
358 port, servPort->s_name);
360 if (++port > last_port)
363 dbprintf(_("bind_portrange: all ports between %d and %d busy\n"),
375 return (match("[ \t\f\r\n\"]", str) != 0);
380 * For backward compatibility we are trying for minimal quoting.
381 * We only quote a string if it contains whitespace or is misquoted...
391 if ((str == NULL) || (*str == '\0')) {
392 ret = stralloc("\"\"");
393 } else if ((match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) {
395 * String does not need to be quoted since it contains
396 * neither whitespace, control or quote characters.
401 * Allocate maximum possible string length.
402 * (a string of all quotes plus room for leading ", trailing " and NULL)
404 ret = s = alloc((strlen(str) * 2) + 2 + 1);
406 while (*str != '\0') {
412 } else if (*str == '\n') {
417 } else if (*str == '\r') {
422 } else if (*str == '\f') {
427 } else if (*str == '\\') {
450 if ((str == NULL) || (*str == '\0')) {
456 ret = in = out = stralloc(str);
457 while (*in != '\0') {
469 } else if (*in == 't') {
473 } else if (*in == 'r') {
477 } else if (*in == 'f') {
497 if ((str == NULL) || (*str == '\0')) {
501 for (s = ret; *s != '\0'; s++) {
502 if (iscntrl((int)*s))
510 Return 0 if the following characters are present
511 * ( ) < > [ ] , ; : ! $ \ / "
519 return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
533 if ((infd = open(src, O_RDONLY)) == -1) {
535 quoted = quote_string(src);
536 *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
537 quoted, strerror(save_errno));
542 if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
544 quoted = quote_string(dst);
545 *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
546 quoted, strerror(save_errno));
552 while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
553 if(fullwrite(outfd,&buf,(size_t)nb) < nb) {
555 quoted = quote_string(dst);
556 *errmsg = vstrallocf(_("Error writing to '%s': %s"),
557 quoted, strerror(save_errno));
567 quoted = quote_string(src);
568 *errmsg = vstrallocf(_("Error reading from '%s': %s"),
569 quoted, strerror(save_errno));
581 #ifndef HAVE_READLINE
583 * simple readline() replacements, used when we don't have readline
584 * support from the system.
591 g_printf("%s", prompt);
601 (void)line; /* Quiet unused parameter warning */
606 /* Order of preference: readdir64(), readdir(). */
607 #if HAVE_DECL_READDIR64
608 # define USE_DIRENT64
609 # define USE_READDIR64
610 #elif HAVE_DECL_READDIR
613 # error No readdir() or readdir64() available!
616 char * portable_readdir(DIR* handle) {
619 struct dirent64 *entry_p;
621 struct dirent *entry_p;
624 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
626 g_static_mutex_lock(&mutex);
629 entry_p = readdir(handle);
632 entry_p = readdir64(handle);
635 g_static_mutex_unlock(&mutex);
640 /* FIXME: According to glibc documentation, d_name may not be
641 null-terminated in some cases on some very old platforms. Not
642 sure what to do about that case. */
643 return strdup(entry_p->d_name);
646 int search_directory(DIR * handle, const char * regex,
647 SearchDirectoryFunctor functor, gpointer user_data) {
649 regex_t compiled_regex;
650 gboolean done = FALSE;
652 if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
653 regfree(&compiled_regex);
662 read_name = portable_readdir(handle);
663 if (read_name == NULL) {
664 regfree(&compiled_regex);
667 result = regexec(&compiled_regex, read_name, 0, NULL, 0);
670 done = !functor(read_name, user_data);
674 regfree(&compiled_regex);
678 char* find_regex_substring(const char* base_string, const regmatch_t match) {
682 size = match.rm_eo - match.rm_so;
683 rval = malloc(size+1);
684 memcpy(rval, base_string + match.rm_so, size);
690 int compare_possibly_null_strings(const char * a, const char * b) {
692 /* NULL or otherwise, they're the same. */
694 } else if (a == NULL) {
697 } else if (b == NULL) {
706 gboolean amanda_thread_init(void) {
707 gboolean success = FALSE;
709 static gboolean did_curl_init = FALSE;
710 if (!did_curl_init) {
711 # ifdef G_THREADS_ENABLED
712 g_assert(!g_thread_supported());
714 g_assert(curl_global_init(CURL_GLOBAL_ALL) == 0);
715 did_curl_init = TRUE;
718 #if defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
719 if (g_thread_supported()) {
729 resolve_hostname(const char *hostname,
731 struct addrinfo **res,
734 struct addrinfo hints;
735 struct addrinfo *myres;
739 if (res) *res = NULL;
742 flags = AI_CANONNAME;
746 flags |= AI_ADDRCONFIG;
749 memset(&hints, 0, sizeof(hints));
750 hints.ai_family = AF_UNSPEC;
751 hints.ai_flags = flags;
752 hints.ai_socktype = socktype;
753 result = getaddrinfo(hostname, NULL, &hints, &myres);
758 if (canonname && myres && myres->ai_canonname) {
759 *canonname = stralloc(myres->ai_canonname);
776 if (WIFEXITED(status)) {
777 int exitstatus = WEXITSTATUS(status);
779 return vstrallocf(_("%s exited normally"), subject);
781 return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
784 if (WIFSIGNALED(status)) {
785 int signal = WTERMSIG(status);
787 if (WCOREDUMP(status))
788 return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
792 return vstrallocf(_("%s exited after receiving signal %d"),
796 if (WIFSTOPPED(status)) {
797 int signal = WSTOPSIG(status);
798 return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
803 if (WIFCONTINUED(status)) {
804 return vstrallocf(_("%s was resumed"), subject);
808 return vstrallocf(_("%s exited in unknown circumstances"), subject);
812 check_running_as(running_as_flags who)
818 char *uname_me = NULL;
819 char *uname_target = NULL;
823 if ((pw = getpwuid(uid_me)) == NULL) {
824 error(_("current userid %ld not found in password database"), (long)uid_me);
827 uname_me = stralloc(pw->pw_name);
829 #ifndef SINGLE_USERID
830 if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
831 error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
832 (long long int)geteuid(), (long long int)uid_me);
837 switch (who & RUNNING_AS_USER_MASK) {
838 case RUNNING_AS_ROOT:
840 uname_target = "root";
843 case RUNNING_AS_DUMPUSER_PREFERRED:
844 dumpuser = getconf_str(CNF_DUMPUSER);
845 if ((pw = getpwnam(dumpuser)) != NULL &&
846 uid_me != pw->pw_uid) {
847 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
848 uid_me == pw->pw_uid) {
849 /* uid == CLIENT_LOGIN: not ideal, but OK */
850 dbprintf(_("NOTE: running as '%s', which is the client"
851 " user, not the dumpuser ('%s'); forging"
853 CLIENT_LOGIN, dumpuser);
854 uid_target = uid_me; /* force success below */
860 case RUNNING_AS_DUMPUSER:
861 uname_target = getconf_str(CNF_DUMPUSER);
862 if ((pw = getpwnam(uname_target)) == NULL) {
863 error(_("cannot look up dumpuser \"%s\""), uname_target);
866 uid_target = pw->pw_uid;
869 case RUNNING_AS_CLIENT_LOGIN:
870 uname_target = CLIENT_LOGIN;
871 if ((pw = getpwnam(uname_target)) == NULL) {
872 error(_("cannot look up client user \"%s\""), uname_target);
875 uid_target = pw->pw_uid;
879 error(_("Unknown check_running_as() call"));
883 if (uid_me != uid_target) {
884 error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
890 /* Quiet unused variable warning */
896 set_root_privs(int need_root)
898 #ifndef SINGLE_USERID
900 if (seteuid(0) == -1) return 0;
901 /* (we don't switch the group back) */
903 if (geteuid() != 0) return 0;
904 if (seteuid(getuid()) == -1) return 0;
905 if (setegid(getgid()) == -1) return 0;
908 (void)need_root; /* Quiet unused variable warning */
916 #ifndef SINGLE_USERID
917 if (setuid(0) == -1) return 0;
926 /* current process name */
927 #define MAX_PNAME 128
928 static char pname[MAX_PNAME] = "unknown";
933 g_strlcpy(pname, p, sizeof(pname));