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;
176 int save_errno = EAGAIN;
178 assert(first_port <= last_port);
179 /* Try a port already used */
180 for(i=0; i < nb_port_in_use; i++) {
181 port = port_in_use[i];
182 if(port >= first_port && port <= last_port) {
183 s = connect_port(addrp, port, proto, svaddr, nonblock);
184 if(s == -2) return -1;
188 if (errno != EAGAIN && errno != EBUSY)
193 /* Try a port in the range */
194 for (port = first_port; port <= last_port; port++) {
195 s = connect_port(addrp, port, proto, svaddr, nonblock);
196 if(s == -2) return -1;
198 port_in_use[nb_port_in_use++] = port;
201 if (errno != EAGAIN && errno != EBUSY)
205 dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
212 /* addrp is my address */
213 /* svaddr is the address of the remote machine */
214 /* return -2: Don't try again */
215 /* return -1: Try with another port */
216 /* return >0: this is the connected socket */
219 struct sockaddr_storage *addrp,
222 struct sockaddr_storage *svaddr,
226 struct servent * servPort;
231 servPort = getservbyport((int)htons(port), proto);
232 if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
233 dbprintf(_("connect_port: Skip port %d: owned by %s.\n"),
234 port, servPort->s_name);
239 if ((s = make_socket(addrp->ss_family)) == -1) return -2;
241 SS_SET_PORT(addrp, port);
242 socklen = SS_LEN(addrp);
243 if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
246 if(servPort == NULL) {
247 dbprintf(_("connect_port: Try port %d: available - %s\n"),
248 port, strerror(errno));
250 dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"),
251 port, servPort->s_name, strerror(errno));
253 if (save_errno != EADDRINUSE) {
261 if(servPort == NULL) {
262 dbprintf(_("connect_port: Try port %d: available - Success\n"), port);
264 dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"),
265 port, servPort->s_name);
268 /* find out what port was actually used */
270 len = sizeof(*addrp);
271 if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
273 dbprintf(_("connect_port: getsockname() failed: %s\n"),
274 strerror(save_errno));
281 fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
282 if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
284 dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
286 strerror(save_errno));
287 dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
288 str_sockaddr(svaddr),
289 strerror(save_errno));
292 if (save_errno == ECONNREFUSED ||
293 save_errno == EHOSTUNREACH ||
294 save_errno == ENETUNREACH ||
295 save_errno == ETIMEDOUT) {
301 dbprintf(_("connected to %s\n"),
302 str_sockaddr(svaddr));
303 dbprintf(_("our side is %s\n"),
304 str_sockaddr(addrp));
310 * Bind to a port in the given range. Takes a begin,end pair of port numbers.
312 * Returns negative on error (EGAIN if all ports are in use). Returns 0
318 struct sockaddr_storage *addrp,
319 in_port_t first_port,
326 struct servent *servPort;
327 const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
328 int save_errno = EAGAIN;
330 assert(first_port <= last_port);
333 * We pick a different starting port based on our pid and the current
334 * time to avoid always picking the same reserved port twice.
336 port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
339 * Scan through the range, trying all available ports that are either
340 * not taken in /etc/services or registered for *amanda*. Wrap around
341 * if we don't happen to start at the beginning.
343 for (cnt = 0; cnt < num_ports; cnt++) {
344 servPort = getservbyport((int)htons(port), proto);
345 if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
346 SS_SET_PORT(addrp, port);
347 socklen = SS_LEN(addrp);
348 if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) {
349 if (servPort == NULL) {
350 dbprintf(_("bind_portrange2: Try port %d: Available - Success\n"), port);
352 dbprintf(_("bind_portrange2: Try port %d: Owned by %s - Success.\n"), port, servPort->s_name);
356 if (errno != EAGAIN && errno != EBUSY)
358 if (servPort == NULL) {
359 dbprintf(_("bind_portrange2: Try port %d: Available - %s\n"),
360 port, strerror(errno));
362 dbprintf(_("bind_portrange2: Try port %d: Owned by %s - %s\n"),
363 port, servPort->s_name, strerror(errno));
366 dbprintf(_("bind_portrange2: Skip port %d: Owned by %s.\n"),
367 port, servPort->s_name);
369 if (++port > last_port)
372 dbprintf(_("bind_portrange: all ports between %d and %d busy\n"),
384 return (match("[ \t\f\r\n\"]", str) != 0);
389 * For backward compatibility we are trying for minimal quoting.
390 * We only quote a string if it contains whitespace or is misquoted...
400 if ((str == NULL) || (*str == '\0')) {
401 ret = stralloc("\"\"");
402 } else if ((match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) {
404 * String does not need to be quoted since it contains
405 * neither whitespace, control or quote characters.
410 * Allocate maximum possible string length.
411 * (a string of all quotes plus room for leading ", trailing " and NULL)
413 ret = s = alloc((strlen(str) * 2) + 2 + 1);
415 while (*str != '\0') {
421 } else if (*str == '\n') {
426 } else if (*str == '\r') {
431 } else if (*str == '\f') {
436 } else if (*str == '\\') {
459 if ((str == NULL) || (*str == '\0')) {
465 ret = in = out = stralloc(str);
466 while (*in != '\0') {
478 } else if (*in == 't') {
482 } else if (*in == 'r') {
486 } else if (*in == 'f') {
506 if ((str == NULL) || (*str == '\0')) {
510 for (s = ret; *s != '\0'; s++) {
511 if (iscntrl((int)*s))
519 Return 0 if the following characters are present
520 * ( ) < > [ ] , ; : ! $ \ / "
528 return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
542 if ((infd = open(src, O_RDONLY)) == -1) {
544 quoted = quote_string(src);
545 *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
546 quoted, strerror(save_errno));
551 if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
553 quoted = quote_string(dst);
554 *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
555 quoted, strerror(save_errno));
561 while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
562 if(fullwrite(outfd,&buf,(size_t)nb) < nb) {
564 quoted = quote_string(dst);
565 *errmsg = vstrallocf(_("Error writing to '%s': %s"),
566 quoted, strerror(save_errno));
576 quoted = quote_string(src);
577 *errmsg = vstrallocf(_("Error reading from '%s': %s"),
578 quoted, strerror(save_errno));
590 #ifndef HAVE_READLINE
592 * simple readline() replacements, used when we don't have readline
593 * support from the system.
600 g_printf("%s", prompt);
610 (void)line; /* Quiet unused parameter warning */
615 /* Order of preference: readdir64(), readdir(). */
616 #if HAVE_DECL_READDIR64
617 # define USE_DIRENT64
618 # define USE_READDIR64
619 #elif HAVE_DECL_READDIR
622 # error No readdir() or readdir64() available!
625 char * portable_readdir(DIR* handle) {
628 struct dirent64 *entry_p;
630 struct dirent *entry_p;
633 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
635 g_static_mutex_lock(&mutex);
638 entry_p = readdir(handle);
641 entry_p = readdir64(handle);
644 g_static_mutex_unlock(&mutex);
649 /* FIXME: According to glibc documentation, d_name may not be
650 null-terminated in some cases on some very old platforms. Not
651 sure what to do about that case. */
652 return strdup(entry_p->d_name);
655 int search_directory(DIR * handle, const char * regex,
656 SearchDirectoryFunctor functor, gpointer user_data) {
658 regex_t compiled_regex;
659 gboolean done = FALSE;
661 if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
662 regfree(&compiled_regex);
671 read_name = portable_readdir(handle);
672 if (read_name == NULL) {
673 regfree(&compiled_regex);
676 result = regexec(&compiled_regex, read_name, 0, NULL, 0);
679 done = !functor(read_name, user_data);
683 regfree(&compiled_regex);
687 char* find_regex_substring(const char* base_string, const regmatch_t match) {
691 size = match.rm_eo - match.rm_so;
692 rval = malloc(size+1);
693 memcpy(rval, base_string + match.rm_so, size);
699 int compare_possibly_null_strings(const char * a, const char * b) {
701 /* NULL or otherwise, they're the same. */
703 } else if (a == NULL) {
706 } else if (b == NULL) {
715 gboolean amanda_thread_init(void) {
716 gboolean success = FALSE;
718 static gboolean did_curl_init = FALSE;
719 if (!did_curl_init) {
720 # ifdef G_THREADS_ENABLED
721 g_assert(!g_thread_supported());
723 g_assert(curl_global_init(CURL_GLOBAL_ALL) == 0);
724 did_curl_init = TRUE;
727 #if defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
728 if (g_thread_supported()) {
738 resolve_hostname(const char *hostname,
740 struct addrinfo **res,
743 struct addrinfo hints;
744 struct addrinfo *myres;
748 if (res) *res = NULL;
751 flags = AI_CANONNAME;
755 flags |= AI_ADDRCONFIG;
758 memset(&hints, 0, sizeof(hints));
759 hints.ai_family = AF_UNSPEC;
760 hints.ai_flags = flags;
761 hints.ai_socktype = socktype;
762 result = getaddrinfo(hostname, NULL, &hints, &myres);
767 if (canonname && myres && myres->ai_canonname) {
768 *canonname = stralloc(myres->ai_canonname);
785 if (WIFEXITED(status)) {
786 int exitstatus = WEXITSTATUS(status);
788 return vstrallocf(_("%s exited normally"), subject);
790 return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
793 if (WIFSIGNALED(status)) {
794 int signal = WTERMSIG(status);
796 if (WCOREDUMP(status))
797 return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
801 return vstrallocf(_("%s exited after receiving signal %d"),
805 if (WIFSTOPPED(status)) {
806 int signal = WSTOPSIG(status);
807 return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
812 if (WIFCONTINUED(status)) {
813 return vstrallocf(_("%s was resumed"), subject);
817 return vstrallocf(_("%s exited in unknown circumstances"), subject);
821 check_running_as(running_as_flags who)
827 char *uname_me = NULL;
828 char *uname_target = NULL;
832 if ((pw = getpwuid(uid_me)) == NULL) {
833 error(_("current userid %ld not found in password database"), (long)uid_me);
836 uname_me = stralloc(pw->pw_name);
838 #ifndef SINGLE_USERID
839 if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
840 error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
841 (long long int)geteuid(), (long long int)uid_me);
846 switch (who & RUNNING_AS_USER_MASK) {
847 case RUNNING_AS_ROOT:
849 uname_target = "root";
852 case RUNNING_AS_DUMPUSER_PREFERRED:
853 dumpuser = getconf_str(CNF_DUMPUSER);
854 if ((pw = getpwnam(dumpuser)) != NULL &&
855 uid_me != pw->pw_uid) {
856 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
857 uid_me == pw->pw_uid) {
858 /* uid == CLIENT_LOGIN: not ideal, but OK */
859 dbprintf(_("NOTE: running as '%s', which is the client"
860 " user, not the dumpuser ('%s'); forging"
862 CLIENT_LOGIN, dumpuser);
863 uid_target = uid_me; /* force success below */
869 case RUNNING_AS_DUMPUSER:
870 uname_target = getconf_str(CNF_DUMPUSER);
871 if ((pw = getpwnam(uname_target)) == NULL) {
872 error(_("cannot look up dumpuser \"%s\""), uname_target);
875 uid_target = pw->pw_uid;
878 case RUNNING_AS_CLIENT_LOGIN:
879 uname_target = CLIENT_LOGIN;
880 if ((pw = getpwnam(uname_target)) == NULL) {
881 error(_("cannot look up client user \"%s\""), uname_target);
884 uid_target = pw->pw_uid;
888 error(_("Unknown check_running_as() call"));
892 if (uid_me != uid_target) {
893 error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
899 /* Quiet unused variable warning */
905 set_root_privs(int need_root)
907 #ifndef SINGLE_USERID
909 if (seteuid(0) == -1) return 0;
910 /* (we don't switch the group back) */
912 if (geteuid() != 0) return 0;
913 if (seteuid(getuid()) == -1) return 0;
914 if (setegid(getgid()) == -1) return 0;
917 (void)need_root; /* Quiet unused variable warning */
925 #ifndef SINGLE_USERID
926 if (setuid(0) == -1) return 0;
935 /* current process name */
936 #define MAX_PNAME 128
937 static char pname[MAX_PNAME] = "unknown";
942 g_strlcpy(pname, p, sizeof(pname));