2007-07-19 16:22:40 +00:00
|
|
|
/*
|
2012-12-13 17:44:57 +00:00
|
|
|
* virutil.c: common, generic utility functions
|
2007-07-19 16:22:40 +00:00
|
|
|
*
|
2014-03-18 08:14:35 +00:00
|
|
|
* Copyright (C) 2006-2014 Red Hat, Inc.
|
2007-07-19 16:22:40 +00:00
|
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
|
|
* Copyright (C) 2006, 2007 Binary Karma
|
|
|
|
* Copyright (C) 2006 Shuveb Hussain
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2007-07-19 16:22:40 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
* File created Jul 18, 2007 - Shuveb Hussain <shuveb@binarykarma.com>
|
|
|
|
*/
|
|
|
|
|
2008-01-29 18:15:54 +00:00
|
|
|
#include <config.h>
|
2007-12-03 16:19:41 +00:00
|
|
|
|
2013-05-16 09:49:12 +00:00
|
|
|
#include <stdlib.h>
|
2013-03-25 16:43:40 +00:00
|
|
|
#include <dirent.h>
|
2007-07-19 16:22:40 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
2008-11-05 18:57:42 +00:00
|
|
|
#include <poll.h>
|
2007-12-03 14:30:46 +00:00
|
|
|
#include <sys/stat.h>
|
2016-04-14 13:11:45 +00:00
|
|
|
|
|
|
|
#ifdef MAJOR_IN_MKDEV
|
|
|
|
# include <sys/mkdev.h>
|
|
|
|
#elif MAJOR_IN_SYSMACROS
|
|
|
|
# include <sys/sysmacros.h>
|
|
|
|
#endif
|
|
|
|
|
Make sure sys/types.h is included after sys/sysmacros.h
In the latest glibc, major() and minor() functions are marked as
deprecated (glibc commit dbab6577):
CC util/libvirt_util_la-vircgroup.lo
util/vircgroup.c: In function 'virCgroupGetBlockDevString':
util/vircgroup.c:768:5: error: '__major_from_sys_types' is deprecated:
In the GNU C Library, `major' is defined by <sys/sysmacros.h>.
For historical compatibility, it is currently defined by
<sys/types.h> as well, but we plan to remove this soon.
To use `major', include <sys/sysmacros.h> directly.
If you did not intend to use a system-defined macro `major',
you should #undef it after including <sys/types.h>.
[-Werror=deprecated-declarations]
if (virAsprintf(&ret, "%d:%d ", major(sb.st_rdev), minor(sb.st_rdev)) < 0)
^~
In file included from /usr/include/features.h:397:0,
from /usr/include/bits/libc-header-start.h:33,
from /usr/include/stdio.h:28,
from ../gnulib/lib/stdio.h:43,
from util/vircgroup.c:26:
/usr/include/sys/sysmacros.h:87:1: note: declared here
__SYSMACROS_DEFINE_MAJOR (__SYSMACROS_FST_IMPL_TEMPL)
^
Moreover, in the glibc commit, there's suggestion to keep
ordering of including of header files as implemented here.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2016-09-06 12:20:04 +00:00
|
|
|
#include <sys/types.h>
|
2009-01-20 16:36:34 +00:00
|
|
|
#include <sys/ioctl.h>
|
2007-12-03 16:19:41 +00:00
|
|
|
#include <string.h>
|
2010-09-22 18:08:58 +00:00
|
|
|
#include <termios.h>
|
2012-08-11 19:13:00 +00:00
|
|
|
#include <locale.h>
|
2011-11-03 20:56:13 +00:00
|
|
|
|
2011-02-18 19:00:47 +00:00
|
|
|
#if HAVE_LIBDEVMAPPER_H
|
|
|
|
# include <libdevmapper.h>
|
|
|
|
#endif
|
2007-12-03 16:19:41 +00:00
|
|
|
|
2009-01-07 10:43:16 +00:00
|
|
|
#include <netdb.h>
|
2009-01-22 19:41:48 +00:00
|
|
|
#ifdef HAVE_GETPWUID_R
|
2010-03-09 18:22:22 +00:00
|
|
|
# include <pwd.h>
|
|
|
|
# include <grp.h>
|
2009-01-22 19:41:48 +00:00
|
|
|
#endif
|
2012-09-20 14:17:56 +00:00
|
|
|
#if WITH_CAPNG
|
2010-03-09 18:22:22 +00:00
|
|
|
# include <cap-ng.h>
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
# include <sys/prctl.h>
|
2009-06-29 17:00:52 +00:00
|
|
|
#endif
|
2007-12-07 14:45:39 +00:00
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
#ifdef WIN32
|
2012-06-21 13:37:34 +00:00
|
|
|
# ifdef HAVE_WINSOCK2_H
|
|
|
|
# include <winsock2.h>
|
|
|
|
# endif
|
2012-05-24 13:37:54 +00:00
|
|
|
# include <windows.h>
|
|
|
|
# include <shlobj.h>
|
|
|
|
#endif
|
|
|
|
|
2011-11-03 20:56:13 +00:00
|
|
|
#include "c-ctype.h"
|
2013-05-21 23:47:48 +00:00
|
|
|
#include "mgetgroups.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2010-04-29 03:36:03 +00:00
|
|
|
#include "verify.h"
|
2013-05-09 18:59:04 +00:00
|
|
|
#include "virfile.h"
|
|
|
|
#include "vircommand.h"
|
|
|
|
#include "nonblocking.h"
|
|
|
|
#include "virprocess.h"
|
|
|
|
#include "virstring.h"
|
|
|
|
#include "virutil.h"
|
2007-12-03 14:30:46 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
verify(sizeof(gid_t) <= sizeof(unsigned int) &&
|
|
|
|
sizeof(uid_t) <= sizeof(unsigned int));
|
2011-07-06 13:39:21 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
2011-07-06 13:39:21 +00:00
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("util.util");
|
|
|
|
|
2014-06-27 15:16:54 +00:00
|
|
|
VIR_ENUM_IMPL(virTristateBool, VIR_TRISTATE_BOOL_LAST,
|
|
|
|
"default",
|
|
|
|
"yes",
|
|
|
|
"no")
|
|
|
|
|
2014-06-27 15:18:53 +00:00
|
|
|
VIR_ENUM_IMPL(virTristateSwitch, VIR_TRISTATE_SWITCH_LAST,
|
|
|
|
"default",
|
|
|
|
"on",
|
|
|
|
"off")
|
|
|
|
|
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
#ifndef WIN32
|
2007-12-03 14:30:46 +00:00
|
|
|
|
2014-03-18 08:14:35 +00:00
|
|
|
int virSetInherit(int fd, bool inherit)
|
|
|
|
{
|
2013-05-09 18:59:04 +00:00
|
|
|
int fflags;
|
|
|
|
if ((fflags = fcntl(fd, F_GETFD)) < 0)
|
2011-07-05 21:02:53 +00:00
|
|
|
return -1;
|
2013-05-09 18:59:04 +00:00
|
|
|
if (inherit)
|
|
|
|
fflags &= ~FD_CLOEXEC;
|
|
|
|
else
|
|
|
|
fflags |= FD_CLOEXEC;
|
|
|
|
if ((fcntl(fd, F_SETFD, fflags)) < 0)
|
2011-07-05 21:02:53 +00:00
|
|
|
return -1;
|
2007-12-03 14:30:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
#else /* WIN32 */
|
2012-07-10 11:24:04 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
int virSetInherit(int fd ATTRIBUTE_UNUSED, bool inherit ATTRIBUTE_UNUSED)
|
2010-01-26 13:47:02 +00:00
|
|
|
{
|
2013-05-09 18:59:04 +00:00
|
|
|
/* FIXME: Currently creating child processes is not supported on
|
|
|
|
* Win32, so there is no point in failing calls that are only relevant
|
|
|
|
* when creating child processes. So just pretend that we changed the
|
|
|
|
* inheritance property of the given fd as requested. */
|
|
|
|
return 0;
|
2010-01-26 13:47:02 +00:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
#endif /* WIN32 */
|
2011-04-03 09:21:14 +00:00
|
|
|
|
2014-03-18 08:14:35 +00:00
|
|
|
int virSetBlocking(int fd, bool blocking)
|
|
|
|
{
|
2013-05-09 18:59:04 +00:00
|
|
|
return set_nonblocking_flag(fd, !blocking);
|
2007-12-03 14:30:46 +00:00
|
|
|
}
|
|
|
|
|
2014-03-18 08:14:35 +00:00
|
|
|
int virSetNonBlock(int fd)
|
|
|
|
{
|
2013-05-09 18:59:04 +00:00
|
|
|
return virSetBlocking(fd, false);
|
2011-10-20 09:02:27 +00:00
|
|
|
}
|
2008-08-13 10:52:15 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
int virSetCloseExec(int fd)
|
2012-05-24 13:37:54 +00:00
|
|
|
{
|
2013-05-09 18:59:04 +00:00
|
|
|
return virSetInherit(fd, false);
|
2012-05-24 13:37:54 +00:00
|
|
|
}
|
|
|
|
|
2014-04-25 16:47:08 +00:00
|
|
|
#ifdef WIN32
|
2014-09-07 15:05:03 +00:00
|
|
|
int virSetSockReuseAddr(int fd ATTRIBUTE_UNUSED, bool fatal ATTRIBUTE_UNUSED)
|
2014-04-25 16:47:08 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* SO_REUSEADDR on Windows is actually akin to SO_REUSEPORT
|
|
|
|
* on Linux/BSD. ie it allows 2 apps to listen to the same
|
|
|
|
* port at once which is certainly not what we want here.
|
|
|
|
*
|
|
|
|
* Win32 sockets have Linux/BSD-like SO_REUSEADDR behaviour
|
|
|
|
* by default, so we can be a no-op.
|
|
|
|
*
|
|
|
|
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
2014-09-07 15:05:03 +00:00
|
|
|
int virSetSockReuseAddr(int fd, bool fatal)
|
2014-04-25 16:47:08 +00:00
|
|
|
{
|
|
|
|
int opt = 1;
|
2014-09-07 15:05:03 +00:00
|
|
|
int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
|
|
|
|
|
|
if (ret < 0 && fatal) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Unable to set socket reuse addr flag"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2014-04-25 16:47:08 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
int
|
|
|
|
virPipeReadUntilEOF(int outfd, int errfd,
|
|
|
|
char **outbuf, char **errbuf) {
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
struct pollfd fds[2];
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-05-24 10:14:02 +00:00
|
|
|
bool finished[2];
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
fds[0].fd = outfd;
|
|
|
|
fds[0].events = POLLIN;
|
|
|
|
fds[0].revents = 0;
|
2013-05-24 10:14:02 +00:00
|
|
|
finished[0] = false;
|
2013-05-09 18:59:04 +00:00
|
|
|
fds[1].fd = errfd;
|
|
|
|
fds[1].events = POLLIN;
|
|
|
|
fds[1].revents = 0;
|
2013-05-24 10:14:02 +00:00
|
|
|
finished[1] = false;
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
while (!(finished[0] && finished[1])) {
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
if (poll(fds, ARRAY_CARDINALITY(fds), -1) < 0) {
|
|
|
|
if ((errno == EAGAIN) || (errno == EINTR))
|
|
|
|
continue;
|
|
|
|
goto pollerr;
|
2012-05-24 13:37:54 +00:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(fds); ++i) {
|
|
|
|
char data[1024], **buf;
|
|
|
|
int got, size;
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
if (!(fds[i].revents))
|
|
|
|
continue;
|
|
|
|
else if (fds[i].revents & POLLHUP)
|
2013-05-24 10:14:02 +00:00
|
|
|
finished[i] = true;
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
if (!(fds[i].revents & POLLIN)) {
|
|
|
|
if (fds[i].revents & POLLHUP)
|
|
|
|
continue;
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("Unknown poll response."));
|
|
|
|
goto error;
|
|
|
|
}
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
got = read(fds[i].fd, data, sizeof(data));
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
if (got == sizeof(data))
|
2013-05-24 10:14:02 +00:00
|
|
|
finished[i] = false;
|
2009-10-08 14:55:58 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
if (got == 0) {
|
2013-05-24 10:14:02 +00:00
|
|
|
finished[i] = true;
|
2013-05-09 18:59:04 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (got < 0) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
if (errno == EAGAIN)
|
|
|
|
break;
|
|
|
|
goto pollerr;
|
|
|
|
}
|
2009-10-08 14:55:58 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
buf = ((fds[i].fd == outfd) ? outbuf : errbuf);
|
|
|
|
size = (*buf ? strlen(*buf) : 0);
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_REALLOC_N(*buf, size+got+1) < 0)
|
2013-05-09 18:59:04 +00:00
|
|
|
goto error;
|
|
|
|
memmove(*buf+size, data, got);
|
|
|
|
(*buf)[size+got] = '\0';
|
2009-10-08 14:55:58 +00:00
|
|
|
}
|
2013-05-09 18:59:04 +00:00
|
|
|
continue;
|
2010-05-20 15:41:31 +00:00
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
pollerr:
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s", _("poll error"));
|
|
|
|
goto error;
|
2010-05-20 15:41:31 +00:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:59:04 +00:00
|
|
|
return 0;
|
2010-05-20 15:41:31 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
error:
|
2013-05-09 18:59:04 +00:00
|
|
|
VIR_FREE(*outbuf);
|
|
|
|
VIR_FREE(*errbuf);
|
|
|
|
return -1;
|
2010-05-20 15:41:31 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 21:13:45 +00:00
|
|
|
/* Convert C from hexadecimal character to integer. */
|
|
|
|
int
|
|
|
|
virHexToBin(unsigned char c)
|
|
|
|
{
|
|
|
|
switch (c) {
|
|
|
|
default: return c - '0';
|
|
|
|
case 'a': case 'A': return 10;
|
|
|
|
case 'b': case 'B': return 11;
|
|
|
|
case 'c': case 'C': return 12;
|
|
|
|
case 'd': case 'D': return 13;
|
|
|
|
case 'e': case 'E': return 14;
|
|
|
|
case 'f': case 'F': return 15;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-05 16:28:59 +00:00
|
|
|
/* Scale an integer VALUE in-place by an optional case-insensitive
|
|
|
|
* SUFFIX, defaulting to SCALE if suffix is NULL or empty (scale is
|
|
|
|
* typically 1 or 1024). Recognized suffixes include 'b' or 'bytes',
|
|
|
|
* as well as power-of-two scaling via binary abbreviations ('KiB',
|
|
|
|
* 'MiB', ...) or their one-letter counterpart ('k', 'M', ...), and
|
|
|
|
* power-of-ten scaling via SI abbreviations ('KB', 'MB', ...).
|
|
|
|
* Ensure that the result does not exceed LIMIT. Return 0 on success,
|
|
|
|
* -1 with error message raised on failure. */
|
|
|
|
int
|
|
|
|
virScaleInteger(unsigned long long *value, const char *suffix,
|
|
|
|
unsigned long long scale, unsigned long long limit)
|
|
|
|
{
|
|
|
|
if (!suffix || !*suffix) {
|
|
|
|
if (!scale) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("invalid scale %llu"), scale);
|
2012-03-05 16:28:59 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
suffix = "";
|
|
|
|
} else if (STRCASEEQ(suffix, "b") || STRCASEEQ(suffix, "byte") ||
|
|
|
|
STRCASEEQ(suffix, "bytes")) {
|
|
|
|
scale = 1;
|
|
|
|
} else {
|
|
|
|
int base;
|
|
|
|
|
|
|
|
if (!suffix[1] || STRCASEEQ(suffix + 1, "iB")) {
|
|
|
|
base = 1024;
|
|
|
|
} else if (c_tolower(suffix[1]) == 'b' && !suffix[2]) {
|
|
|
|
base = 1000;
|
|
|
|
} else {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
2012-03-05 16:28:59 +00:00
|
|
|
_("unknown suffix '%s'"), suffix);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
scale = 1;
|
|
|
|
switch (c_tolower(*suffix)) {
|
|
|
|
case 'e':
|
|
|
|
scale *= base;
|
|
|
|
/* fallthrough */
|
|
|
|
case 'p':
|
|
|
|
scale *= base;
|
|
|
|
/* fallthrough */
|
|
|
|
case 't':
|
|
|
|
scale *= base;
|
|
|
|
/* fallthrough */
|
|
|
|
case 'g':
|
|
|
|
scale *= base;
|
|
|
|
/* fallthrough */
|
|
|
|
case 'm':
|
|
|
|
scale *= base;
|
|
|
|
/* fallthrough */
|
|
|
|
case 'k':
|
|
|
|
scale *= base;
|
|
|
|
break;
|
|
|
|
default:
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("unknown suffix '%s'"), suffix);
|
2012-03-05 16:28:59 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 15:13:21 +00:00
|
|
|
if (*value && *value > (limit / scale)) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_OVERFLOW, _("value too large: %llu%s"),
|
|
|
|
*value, suffix);
|
2012-03-05 16:28:59 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*value *= scale;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-29 17:47:08 +00:00
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
/**
|
|
|
|
* virParseNumber:
|
|
|
|
* @str: pointer to the char pointer used
|
|
|
|
*
|
|
|
|
* Parse an unsigned number
|
|
|
|
*
|
|
|
|
* Returns the unsigned number or -1 in case of error. @str will be
|
|
|
|
* updated to skip the number.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virParseNumber(const char **str)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
const char *cur = *str;
|
|
|
|
|
|
|
|
if ((*cur < '0') || (*cur > '9'))
|
2012-03-22 11:33:35 +00:00
|
|
|
return -1;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
start using c-ctype functions
Up to now, we've been avoiding ctype functions like isspace, isdigit,
etc. because they are locale-dependent. Now that we have the c-ctype
functions, we can start using *them*, to make the code more readable
with changes like these:
- /* This may not work on EBCDIC. */
- if ((*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ||
- (*p >= '0' && *p <= '9'))
+ if (c_isalnum(*p))
- while ((*cur >= '0') && (*cur <= '9')) {
+ while (c_isdigit(*cur)) {
Also, some macros in conf.c used names that conflicted with
standard meaning of "BLANK" and "SPACE", so I've adjusted them
to be in line with the definition of e.g., isblank.
In addition, I've wrapped those statement macros with do {...} while (0),
so that we can't forget the ";" after a use. There was one like that
already (fixed below). The missing semicolon would mess up automatic
indenting.
* src/buf.c (virBufferURIEncodeString):
* src/conf.c (IS_EOL, SKIP_BLANKS_AND_EOL, SKIP_BLANKS)
(virConfParseLong, virConfParseValue, virConfParseName)
(virConfParseSeparator, virConfParseStatement, IS_BLANK, IS_CHAR)
(IS_DIGIT, IS_SPACE, SKIP_SPACES):
* src/nodeinfo.c:
* src/qemu_conf.c (qemudParseInterfaceXML):
* src/qemu_driver.c (qemudDomainBlockStats):
* src/sexpr.c:
* src/stats_linux.c:
* src/util.c (virParseNumber, virDiskNameToIndex):
* src/uuid.c (hextobin, virUUIDParse):
* src/virsh.c:
* src/xml.c (parseCpuNumber, virParseCpuSet):
2008-05-16 09:37:44 +00:00
|
|
|
while (c_isdigit(*cur)) {
|
2008-02-27 04:35:08 +00:00
|
|
|
unsigned int c = *cur - '0';
|
|
|
|
|
|
|
|
if ((ret > INT_MAX / 10) ||
|
|
|
|
((ret == INT_MAX / 10) && (c > INT_MAX % 10)))
|
2012-03-22 11:33:35 +00:00
|
|
|
return -1;
|
2008-02-27 04:35:08 +00:00
|
|
|
ret = ret * 10 + c;
|
|
|
|
cur++;
|
|
|
|
}
|
|
|
|
*str = cur;
|
2012-03-22 11:33:35 +00:00
|
|
|
return ret;
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2008-02-08 09:15:16 +00:00
|
|
|
|
2010-03-30 14:15:13 +00:00
|
|
|
/**
|
|
|
|
* virParseVersionString:
|
|
|
|
* @str: const char pointer to the version string
|
|
|
|
* @version: unsigned long pointer to output the version number
|
2011-07-01 13:23:02 +00:00
|
|
|
* @allowMissing: true to treat 3 like 3.0.0, false to error out on
|
|
|
|
* missing minor or micro
|
2010-03-30 14:15:13 +00:00
|
|
|
*
|
|
|
|
* Parse an unsigned version number from a version string. Expecting
|
|
|
|
* 'major.minor.micro' format, ignoring an optional suffix.
|
|
|
|
*
|
|
|
|
* The major, minor and micro numbers are encoded into a single version number:
|
|
|
|
*
|
|
|
|
* 1000000 * major + 1000 * minor + micro
|
|
|
|
*
|
|
|
|
* Returns the 0 for success, -1 for error.
|
|
|
|
*/
|
|
|
|
int
|
2011-07-01 13:23:02 +00:00
|
|
|
virParseVersionString(const char *str, unsigned long *version,
|
|
|
|
bool allowMissing)
|
2010-03-30 14:15:13 +00:00
|
|
|
{
|
2011-07-01 10:40:21 +00:00
|
|
|
unsigned int major, minor = 0, micro = 0;
|
2010-03-30 14:15:13 +00:00
|
|
|
char *tmp;
|
|
|
|
|
2011-07-01 10:40:21 +00:00
|
|
|
if (virStrToLong_ui(str, &tmp, 10, &major) < 0)
|
2010-03-30 14:15:13 +00:00
|
|
|
return -1;
|
|
|
|
|
2011-07-01 13:23:02 +00:00
|
|
|
if (!allowMissing && *tmp != '.')
|
|
|
|
return -1;
|
|
|
|
|
2011-07-01 10:40:21 +00:00
|
|
|
if ((*tmp == '.') && virStrToLong_ui(tmp + 1, &tmp, 10, &minor) < 0)
|
2010-03-30 14:15:13 +00:00
|
|
|
return -1;
|
|
|
|
|
2011-07-01 13:23:02 +00:00
|
|
|
if (!allowMissing && *tmp != '.')
|
|
|
|
return -1;
|
|
|
|
|
2011-07-01 10:40:21 +00:00
|
|
|
if ((*tmp == '.') && virStrToLong_ui(tmp + 1, &tmp, 10, µ) < 0)
|
2010-03-30 14:15:13 +00:00
|
|
|
return -1;
|
|
|
|
|
2011-07-01 13:23:02 +00:00
|
|
|
if (major > UINT_MAX / 1000000 || minor > 999 || micro > 999)
|
|
|
|
return -1;
|
|
|
|
|
2010-03-30 14:15:13 +00:00
|
|
|
*version = 1000000 * major + 1000 * minor + micro;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:00:15 +00:00
|
|
|
int virEnumFromString(const char *const*types,
|
|
|
|
unsigned int ntypes,
|
|
|
|
const char *type)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2009-06-22 16:37:52 +00:00
|
|
|
if (!type)
|
|
|
|
return -1;
|
|
|
|
|
2013-05-21 07:58:16 +00:00
|
|
|
for (i = 0; i < ntypes; i++)
|
2008-06-24 15:00:15 +00:00
|
|
|
if (STREQ(types[i], type))
|
|
|
|
return i;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-11 19:13:00 +00:00
|
|
|
/* In case thread-safe locales are available */
|
|
|
|
#if HAVE_NEWLOCALE
|
|
|
|
|
|
|
|
static locale_t virLocale;
|
|
|
|
|
|
|
|
static int
|
|
|
|
virLocaleOnceInit(void)
|
|
|
|
{
|
|
|
|
virLocale = newlocale(LC_ALL_MASK, "C", (locale_t)0);
|
|
|
|
if (!virLocale)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virLocale)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virDoubleToStr
|
|
|
|
*
|
|
|
|
* converts double to string with C locale (thread-safe).
|
|
|
|
*
|
|
|
|
* Returns -1 on error, size of the string otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virDoubleToStr(char **strp, double number)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
#if HAVE_NEWLOCALE
|
|
|
|
|
|
|
|
locale_t old_loc;
|
|
|
|
|
|
|
|
if (virLocaleInitialize() < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
old_loc = uselocale(virLocale);
|
|
|
|
ret = virAsprintf(strp, "%lf", number);
|
|
|
|
uselocale(old_loc);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
char *radix, *tmp;
|
|
|
|
struct lconv *lc;
|
|
|
|
|
2012-08-14 10:12:38 +00:00
|
|
|
if ((ret = virAsprintf(strp, "%lf", number) < 0))
|
2012-08-11 19:13:00 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
lc = localeconv();
|
|
|
|
radix = lc->decimal_point;
|
|
|
|
tmp = strstr(*strp, radix);
|
|
|
|
if (tmp) {
|
|
|
|
*tmp = '.';
|
|
|
|
if (strlen(radix) > 1)
|
2012-08-14 10:12:38 +00:00
|
|
|
memmove(tmp + 1, tmp + strlen(radix), strlen(*strp) - (tmp - *strp));
|
2012-08-11 19:13:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_NEWLOCALE */
|
|
|
|
error:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-25 17:31:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Format @val as a base-10 decimal number, in the
|
|
|
|
* buffer @buf of size @buflen. To allocate a suitable
|
|
|
|
* sized buffer, the INT_BUFLEN(int) macro should be
|
|
|
|
* used
|
|
|
|
*
|
|
|
|
* Returns pointer to start of the number in @buf
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
virFormatIntDecimal(char *buf, size_t buflen, int val)
|
|
|
|
{
|
|
|
|
char *p = buf + buflen - 1;
|
|
|
|
*p = '\0';
|
|
|
|
if (val >= 0) {
|
|
|
|
do {
|
|
|
|
*--p = '0' + (val % 10);
|
|
|
|
val /= 10;
|
|
|
|
} while (val != 0);
|
|
|
|
} else {
|
|
|
|
do {
|
|
|
|
*--p = '0' - (val % 10);
|
|
|
|
val /= 10;
|
|
|
|
} while (val != 0);
|
|
|
|
*--p = '-';
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-06-24 15:00:15 +00:00
|
|
|
const char *virEnumToString(const char *const*types,
|
|
|
|
unsigned int ntypes,
|
|
|
|
int type)
|
|
|
|
{
|
|
|
|
if (type < 0 || type >= ntypes)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return types[type];
|
|
|
|
}
|
|
|
|
|
2010-03-19 17:26:09 +00:00
|
|
|
/* Translates a device name of the form (regex) /^[fhv]d[a-z]+[0-9]*$/
|
2015-11-13 13:14:44 +00:00
|
|
|
* into the corresponding index and partition number
|
|
|
|
* (e.g. sda0 => (0,0), hdz2 => (25,2), vdaa12 => (26,12))
|
2008-05-09 16:41:19 +00:00
|
|
|
* @param name The name of the device
|
2015-11-13 13:14:44 +00:00
|
|
|
* @param disk The disk index to be returned
|
|
|
|
* @param partition The partition index to be returned
|
|
|
|
* @return 0 on success, or -1 on failure
|
2008-05-09 16:41:19 +00:00
|
|
|
*/
|
2015-11-13 13:14:44 +00:00
|
|
|
int virDiskNameParse(const char *name, int *disk, int *partition)
|
2014-03-18 08:14:35 +00:00
|
|
|
{
|
2008-05-09 16:41:19 +00:00
|
|
|
const char *ptr = NULL;
|
2015-11-13 13:14:44 +00:00
|
|
|
char *rem;
|
2008-05-09 16:41:19 +00:00
|
|
|
int idx = 0;
|
2010-08-10 11:46:46 +00:00
|
|
|
static char const* const drive_prefix[] = {"fd", "hd", "vd", "sd", "xvd", "ubd"};
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2008-05-09 16:41:19 +00:00
|
|
|
|
2008-05-15 16:05:41 +00:00
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(drive_prefix); i++) {
|
|
|
|
if (STRPREFIX(name, drive_prefix[i])) {
|
|
|
|
ptr = name + strlen(drive_prefix[i]);
|
2008-05-09 16:41:19 +00:00
|
|
|
break;
|
2008-05-15 16:05:41 +00:00
|
|
|
}
|
2008-05-09 16:41:19 +00:00
|
|
|
}
|
|
|
|
|
2008-05-15 16:05:41 +00:00
|
|
|
if (!ptr)
|
2008-05-09 16:41:19 +00:00
|
|
|
return -1;
|
|
|
|
|
2008-10-10 08:37:35 +00:00
|
|
|
for (i = 0; *ptr; i++) {
|
start using c-ctype functions
Up to now, we've been avoiding ctype functions like isspace, isdigit,
etc. because they are locale-dependent. Now that we have the c-ctype
functions, we can start using *them*, to make the code more readable
with changes like these:
- /* This may not work on EBCDIC. */
- if ((*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ||
- (*p >= '0' && *p <= '9'))
+ if (c_isalnum(*p))
- while ((*cur >= '0') && (*cur <= '9')) {
+ while (c_isdigit(*cur)) {
Also, some macros in conf.c used names that conflicted with
standard meaning of "BLANK" and "SPACE", so I've adjusted them
to be in line with the definition of e.g., isblank.
In addition, I've wrapped those statement macros with do {...} while (0),
so that we can't forget the ";" after a use. There was one like that
already (fixed below). The missing semicolon would mess up automatic
indenting.
* src/buf.c (virBufferURIEncodeString):
* src/conf.c (IS_EOL, SKIP_BLANKS_AND_EOL, SKIP_BLANKS)
(virConfParseLong, virConfParseValue, virConfParseName)
(virConfParseSeparator, virConfParseStatement, IS_BLANK, IS_CHAR)
(IS_DIGIT, IS_SPACE, SKIP_SPACES):
* src/nodeinfo.c:
* src/qemu_conf.c (qemudParseInterfaceXML):
* src/qemu_driver.c (qemudDomainBlockStats):
* src/sexpr.c:
* src/stats_linux.c:
* src/util.c (virParseNumber, virDiskNameToIndex):
* src/uuid.c (hextobin, virUUIDParse):
* src/virsh.c:
* src/xml.c (parseCpuNumber, virParseCpuSet):
2008-05-16 09:37:44 +00:00
|
|
|
if (!c_islower(*ptr))
|
2010-03-19 17:26:09 +00:00
|
|
|
break;
|
2008-05-09 16:41:19 +00:00
|
|
|
|
2012-11-22 14:56:08 +00:00
|
|
|
idx = (idx + (i < 1 ? 0 : 1)) * 26;
|
2008-05-09 16:41:19 +00:00
|
|
|
idx += *ptr - 'a';
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
2010-03-19 17:26:09 +00:00
|
|
|
/* Count the trailing digits. */
|
|
|
|
size_t n_digits = strspn(ptr, "0123456789");
|
|
|
|
if (ptr[n_digits] != '\0')
|
|
|
|
return -1;
|
|
|
|
|
2015-11-13 13:14:44 +00:00
|
|
|
*disk = idx;
|
|
|
|
|
|
|
|
/* Convert trailing digits into our partition index */
|
|
|
|
if (partition) {
|
|
|
|
*partition = 0;
|
|
|
|
|
|
|
|
/* Shouldn't start by zero */
|
|
|
|
if (n_digits > 1 && *ptr == '0')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (n_digits && virStrToLong_i(ptr, &rem, 10, partition) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Translates a device name of the form (regex) /^[fhv]d[a-z]+[0-9]*$/
|
|
|
|
* into the corresponding index (e.g. sda => 0, hdz => 25, vdaa => 26)
|
|
|
|
* Note that any trailing string of digits is simply ignored.
|
|
|
|
* @param name The name of the device
|
|
|
|
* @return name's index, or -1 on failure
|
|
|
|
*/
|
|
|
|
int virDiskNameToIndex(const char *name)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
if (virDiskNameParse(name, &idx, NULL))
|
|
|
|
idx = -1;
|
|
|
|
|
2008-05-09 16:41:19 +00:00
|
|
|
return idx;
|
|
|
|
}
|
2009-01-06 17:46:46 +00:00
|
|
|
|
2009-12-03 16:17:40 +00:00
|
|
|
char *virIndexToDiskName(int idx, const char *prefix)
|
|
|
|
{
|
|
|
|
char *name = NULL;
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
|
|
|
int ctr;
|
|
|
|
int offset;
|
2009-12-03 16:17:40 +00:00
|
|
|
|
|
|
|
if (idx < 0) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Disk index %d is negative"), idx);
|
2009-12-03 16:17:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
for (i = 0, ctr = idx; ctr >= 0; ++i, ctr = ctr / 26 - 1) { }
|
2009-12-03 16:17:40 +00:00
|
|
|
|
|
|
|
offset = strlen(prefix);
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(name, offset + i + 1))
|
2009-12-03 16:17:40 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
strcpy(name, prefix);
|
|
|
|
name[offset + i] = '\0';
|
|
|
|
|
2014-11-13 14:28:18 +00:00
|
|
|
for (i = i - 1, ctr = idx; ctr >= 0; --i, ctr = ctr / 26 - 1)
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
name[offset + i] = 'a' + (ctr % 26);
|
2009-12-03 16:17:40 +00:00
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2009-01-07 10:43:16 +00:00
|
|
|
#ifndef AI_CANONIDN
|
2010-03-09 18:22:22 +00:00
|
|
|
# define AI_CANONIDN 0
|
2009-01-07 10:43:16 +00:00
|
|
|
#endif
|
|
|
|
|
2010-05-20 17:16:30 +00:00
|
|
|
/* Who knew getting a hostname could be so delicate. In Linux (and Unices
|
|
|
|
* in general), many things depend on "hostname" returning a value that will
|
|
|
|
* resolve one way or another. In the modern world where networks frequently
|
|
|
|
* come and go this is often being hard-coded to resolve to "localhost". If
|
|
|
|
* it *doesn't* resolve to localhost, then we would prefer to have the FQDN.
|
|
|
|
* That leads us to 3 possibilities:
|
|
|
|
*
|
|
|
|
* 1) gethostname() returns an FQDN (not localhost) - we return the string
|
|
|
|
* as-is, it's all of the information we want
|
|
|
|
* 2) gethostname() returns "localhost" - we return localhost; doing further
|
|
|
|
* work to try to resolve it is pointless
|
|
|
|
* 3) gethostname() returns a shortened hostname - in this case, we want to
|
|
|
|
* try to resolve this to a fully-qualified name. Therefore we pass it
|
|
|
|
* to getaddrinfo(). There are two possible responses:
|
|
|
|
* a) getaddrinfo() resolves to a FQDN - return the FQDN
|
2011-10-14 14:25:50 +00:00
|
|
|
* b) getaddrinfo() fails or resolves to localhost - in this case, the
|
2011-10-13 14:19:37 +00:00
|
|
|
* data we got from gethostname() is actually more useful than what
|
|
|
|
* we got from getaddrinfo(). Return the value from gethostname()
|
|
|
|
* and hope for the best.
|
2010-05-20 17:16:30 +00:00
|
|
|
*/
|
2015-12-04 17:35:54 +00:00
|
|
|
static char *
|
|
|
|
virGetHostnameImpl(bool quiet)
|
2009-01-07 10:43:16 +00:00
|
|
|
{
|
|
|
|
int r;
|
2015-12-04 17:35:54 +00:00
|
|
|
char hostname[HOST_NAME_MAX+1], *result = NULL;
|
2010-05-20 17:16:30 +00:00
|
|
|
struct addrinfo hints, *info;
|
2009-01-07 10:43:16 +00:00
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
r = gethostname(hostname, sizeof(hostname));
|
2009-10-23 17:01:22 +00:00
|
|
|
if (r == -1) {
|
2015-12-04 17:35:54 +00:00
|
|
|
if (!quiet)
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s", _("failed to determine host name"));
|
2009-01-07 10:43:16 +00:00
|
|
|
return NULL;
|
2009-10-23 17:01:22 +00:00
|
|
|
}
|
2009-01-07 10:43:16 +00:00
|
|
|
NUL_TERMINATE(hostname);
|
|
|
|
|
2010-05-20 17:16:30 +00:00
|
|
|
if (STRPREFIX(hostname, "localhost") || strchr(hostname, '.')) {
|
|
|
|
/* in this case, gethostname returned localhost (meaning we can't
|
|
|
|
* do any further canonicalization), or it returned an FQDN (and
|
|
|
|
* we don't need to do any further canonicalization). Return the
|
|
|
|
* string as-is; it's up to callers to check whether "localhost"
|
|
|
|
* is allowed.
|
|
|
|
*/
|
2015-12-04 17:35:54 +00:00
|
|
|
ignore_value(VIR_STRDUP_QUIET(result, hostname));
|
2013-05-24 07:19:51 +00:00
|
|
|
goto cleanup;
|
2010-05-20 17:16:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise, it's a shortened, non-localhost, hostname. Attempt to
|
|
|
|
* canonicalize the hostname by running it through getaddrinfo
|
|
|
|
*/
|
|
|
|
|
2009-01-07 10:43:16 +00:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_flags = AI_CANONNAME|AI_CANONIDN;
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
r = getaddrinfo(hostname, NULL, &hints, &info);
|
2009-10-23 17:01:22 +00:00
|
|
|
if (r != 0) {
|
2015-12-04 17:35:54 +00:00
|
|
|
if (!quiet)
|
|
|
|
VIR_WARN("getaddrinfo failed for '%s': %s",
|
|
|
|
hostname, gai_strerror(r));
|
|
|
|
ignore_value(VIR_STRDUP_QUIET(result, hostname));
|
2013-05-24 07:19:51 +00:00
|
|
|
goto cleanup;
|
2009-10-23 17:01:22 +00:00
|
|
|
}
|
2010-02-19 15:15:21 +00:00
|
|
|
|
2010-04-07 15:23:24 +00:00
|
|
|
/* Tell static analyzers about getaddrinfo semantics. */
|
2012-10-17 09:23:12 +00:00
|
|
|
sa_assert(info);
|
2010-04-07 15:23:24 +00:00
|
|
|
|
2010-05-20 17:16:30 +00:00
|
|
|
if (info->ai_canonname == NULL ||
|
|
|
|
STRPREFIX(info->ai_canonname, "localhost"))
|
|
|
|
/* in this case, we tried to canonicalize and we ended up back with
|
|
|
|
* localhost. Ignore the canonicalized name and just return the
|
|
|
|
* original hostname
|
|
|
|
*/
|
2015-12-04 17:35:54 +00:00
|
|
|
ignore_value(VIR_STRDUP_QUIET(result, hostname));
|
2010-05-20 17:16:30 +00:00
|
|
|
else
|
|
|
|
/* Caller frees this string. */
|
2015-12-04 17:35:54 +00:00
|
|
|
ignore_value(VIR_STRDUP_QUIET(result, info->ai_canonname));
|
2010-02-19 15:15:21 +00:00
|
|
|
|
2010-05-20 17:16:30 +00:00
|
|
|
freeaddrinfo(info);
|
2009-01-07 10:43:16 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2015-12-04 17:35:54 +00:00
|
|
|
if (!result)
|
|
|
|
virReportOOMError();
|
2009-01-07 10:43:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-07-30 19:55:44 +00:00
|
|
|
|
2015-12-04 17:35:54 +00:00
|
|
|
char *
|
|
|
|
virGetHostname(void)
|
|
|
|
{
|
|
|
|
return virGetHostnameImpl(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virGetHostnameQuiet(void)
|
|
|
|
{
|
|
|
|
return virGetHostnameImpl(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-30 19:55:44 +00:00
|
|
|
char *
|
|
|
|
virGetUserDirectory(void)
|
|
|
|
{
|
|
|
|
return virGetUserDirectoryByUID(geteuid());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-22 19:41:48 +00:00
|
|
|
#ifdef HAVE_GETPWUID_R
|
2013-07-09 23:57:48 +00:00
|
|
|
/* Look up fields from the user database for the given user. On
|
2016-06-17 11:54:52 +00:00
|
|
|
* error, set errno, report the error if not instructed otherwise via @quiet,
|
|
|
|
* and return -1. */
|
2013-07-09 23:57:48 +00:00
|
|
|
static int
|
2016-06-17 11:54:52 +00:00
|
|
|
virGetUserEnt(uid_t uid, char **name, gid_t *group, char **dir, char **shell, bool quiet)
|
2009-01-22 19:41:48 +00:00
|
|
|
{
|
|
|
|
char *strbuf;
|
|
|
|
struct passwd pwbuf;
|
2009-01-30 15:43:05 +00:00
|
|
|
struct passwd *pw = NULL;
|
2010-01-28 12:37:05 +00:00
|
|
|
long val = sysconf(_SC_GETPW_R_SIZE_MAX);
|
|
|
|
size_t strbuflen = val;
|
2011-05-16 21:37:15 +00:00
|
|
|
int rc;
|
2013-07-09 23:57:48 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
*name = NULL;
|
|
|
|
if (dir)
|
|
|
|
*dir = NULL;
|
2016-04-12 17:11:20 +00:00
|
|
|
if (shell)
|
|
|
|
*shell = NULL;
|
2010-01-28 12:37:05 +00:00
|
|
|
|
2011-05-16 21:37:15 +00:00
|
|
|
/* sysconf is a hint; if it fails, fall back to a reasonable size */
|
|
|
|
if (val < 0)
|
|
|
|
strbuflen = 1024;
|
2009-01-22 19:41:48 +00:00
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(strbuf, strbuflen) < 0)
|
2013-07-09 23:57:48 +00:00
|
|
|
return -1;
|
2009-01-22 19:41:48 +00:00
|
|
|
|
2009-01-30 15:43:05 +00:00
|
|
|
/*
|
|
|
|
* From the manpage (terrifying but true):
|
|
|
|
*
|
|
|
|
* ERRORS
|
|
|
|
* 0 or ENOENT or ESRCH or EBADF or EPERM or ...
|
|
|
|
* The given name or uid was not found.
|
|
|
|
*/
|
2011-05-16 21:37:15 +00:00
|
|
|
while ((rc = getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw)) == ERANGE) {
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_RESIZE_N(strbuf, strbuflen, strbuflen, strbuflen) < 0)
|
2013-07-09 23:57:48 +00:00
|
|
|
goto cleanup;
|
2011-05-16 21:37:15 +00:00
|
|
|
}
|
2016-06-17 11:54:52 +00:00
|
|
|
|
2013-09-04 16:15:34 +00:00
|
|
|
if (rc != 0) {
|
2016-06-17 11:54:52 +00:00
|
|
|
if (quiet)
|
|
|
|
goto cleanup;
|
|
|
|
|
2011-05-16 21:37:15 +00:00
|
|
|
virReportSystemError(rc,
|
2010-04-29 03:36:03 +00:00
|
|
|
_("Failed to find user record for uid '%u'"),
|
|
|
|
(unsigned int) uid);
|
2013-07-09 23:57:48 +00:00
|
|
|
goto cleanup;
|
2013-09-04 16:15:34 +00:00
|
|
|
} else if (pw == NULL) {
|
2016-06-17 11:54:52 +00:00
|
|
|
if (quiet)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-09-04 16:15:34 +00:00
|
|
|
virReportError(VIR_ERR_SYSTEM_ERROR,
|
|
|
|
_("Failed to find user record for uid '%u'"),
|
|
|
|
(unsigned int) uid);
|
|
|
|
goto cleanup;
|
2013-07-09 23:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (name && VIR_STRDUP(*name, pw->pw_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (group)
|
|
|
|
*group = pw->pw_gid;
|
2016-04-12 17:11:20 +00:00
|
|
|
if (dir && VIR_STRDUP(*dir, pw->pw_dir) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (shell && VIR_STRDUP(*shell, pw->pw_shell) < 0)
|
2013-07-09 23:57:48 +00:00
|
|
|
goto cleanup;
|
2009-01-22 19:41:48 +00:00
|
|
|
|
2013-07-09 23:57:48 +00:00
|
|
|
ret = 0;
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2016-04-12 17:11:20 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
if (name)
|
|
|
|
VIR_FREE(*name);
|
|
|
|
if (dir)
|
|
|
|
VIR_FREE(*dir);
|
|
|
|
if (shell)
|
|
|
|
VIR_FREE(*shell);
|
|
|
|
}
|
2009-01-22 19:41:48 +00:00
|
|
|
VIR_FREE(strbuf);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2011-12-16 00:55:19 +00:00
|
|
|
static char *virGetGroupEnt(gid_t gid)
|
|
|
|
{
|
|
|
|
char *strbuf;
|
|
|
|
char *ret;
|
|
|
|
struct group grbuf;
|
|
|
|
struct group *gr = NULL;
|
|
|
|
long val = sysconf(_SC_GETGR_R_SIZE_MAX);
|
|
|
|
size_t strbuflen = val;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* sysconf is a hint; if it fails, fall back to a reasonable size */
|
|
|
|
if (val < 0)
|
|
|
|
strbuflen = 1024;
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(strbuf, strbuflen) < 0)
|
2011-12-16 00:55:19 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From the manpage (terrifying but true):
|
|
|
|
*
|
|
|
|
* ERRORS
|
|
|
|
* 0 or ENOENT or ESRCH or EBADF or EPERM or ...
|
|
|
|
* The given name or gid was not found.
|
|
|
|
*/
|
|
|
|
while ((rc = getgrgid_r(gid, &grbuf, strbuf, strbuflen, &gr)) == ERANGE) {
|
|
|
|
if (VIR_RESIZE_N(strbuf, strbuflen, strbuflen, strbuflen) < 0) {
|
|
|
|
VIR_FREE(strbuf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rc != 0 || gr == NULL) {
|
2013-09-04 16:15:34 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
virReportSystemError(rc,
|
|
|
|
_("Failed to find group record for gid '%u'"),
|
|
|
|
(unsigned int) gid);
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_SYSTEM_ERROR,
|
|
|
|
_("Failed to find group record for gid '%u'"),
|
|
|
|
(unsigned int) gid);
|
|
|
|
}
|
|
|
|
|
2011-12-16 00:55:19 +00:00
|
|
|
VIR_FREE(strbuf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-24 07:19:51 +00:00
|
|
|
ignore_value(VIR_STRDUP(ret, gr->gr_name));
|
2011-12-16 00:55:19 +00:00
|
|
|
VIR_FREE(strbuf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-07-30 19:55:44 +00:00
|
|
|
|
|
|
|
char *
|
|
|
|
virGetUserDirectoryByUID(uid_t uid)
|
2009-07-10 10:40:04 +00:00
|
|
|
{
|
2013-07-09 23:57:48 +00:00
|
|
|
char *ret;
|
2016-06-17 11:54:52 +00:00
|
|
|
virGetUserEnt(uid, NULL, NULL, &ret, NULL, false);
|
2016-04-12 17:11:20 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *virGetUserShell(uid_t uid)
|
|
|
|
{
|
|
|
|
char *ret;
|
2016-06-17 11:54:52 +00:00
|
|
|
virGetUserEnt(uid, NULL, NULL, NULL, &ret, false);
|
2013-07-09 23:57:48 +00:00
|
|
|
return ret;
|
2009-07-10 10:40:04 +00:00
|
|
|
}
|
|
|
|
|
2013-07-30 19:55:44 +00:00
|
|
|
|
2012-05-24 12:29:42 +00:00
|
|
|
static char *virGetXDGDirectory(const char *xdgenvname, const char *xdgdefdir)
|
2012-05-03 16:36:27 +00:00
|
|
|
{
|
2013-10-09 10:18:15 +00:00
|
|
|
const char *path = virGetEnvBlockSUID(xdgenvname);
|
2012-05-03 16:36:27 +00:00
|
|
|
char *ret = NULL;
|
2013-06-16 19:33:44 +00:00
|
|
|
char *home = NULL;
|
2012-05-03 16:36:27 +00:00
|
|
|
|
|
|
|
if (path && path[0]) {
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&ret, "%s/libvirt", path));
|
2012-05-03 16:36:27 +00:00
|
|
|
} else {
|
2013-07-09 23:57:48 +00:00
|
|
|
home = virGetUserDirectory();
|
|
|
|
if (home)
|
|
|
|
ignore_value(virAsprintf(&ret, "%s/%s/libvirt", home, xdgdefdir));
|
2012-05-03 16:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(home);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-24 12:29:42 +00:00
|
|
|
char *virGetUserConfigDirectory(void)
|
2012-05-03 16:36:27 +00:00
|
|
|
{
|
2012-05-24 12:29:42 +00:00
|
|
|
return virGetXDGDirectory("XDG_CONFIG_HOME", ".config");
|
2012-05-03 16:36:27 +00:00
|
|
|
}
|
|
|
|
|
2012-05-24 12:29:42 +00:00
|
|
|
char *virGetUserCacheDirectory(void)
|
2012-05-03 16:36:27 +00:00
|
|
|
{
|
2012-05-24 12:29:42 +00:00
|
|
|
return virGetXDGDirectory("XDG_CACHE_HOME", ".cache");
|
2012-05-03 16:36:27 +00:00
|
|
|
}
|
|
|
|
|
2012-05-24 12:29:42 +00:00
|
|
|
char *virGetUserRuntimeDirectory(void)
|
2012-05-03 16:36:27 +00:00
|
|
|
{
|
2013-10-09 10:18:15 +00:00
|
|
|
const char *path = virGetEnvBlockSUID("XDG_RUNTIME_DIR");
|
2012-05-03 16:36:27 +00:00
|
|
|
|
|
|
|
if (!path || !path[0]) {
|
2012-05-24 12:29:42 +00:00
|
|
|
return virGetUserCacheDirectory();
|
2012-05-03 16:36:27 +00:00
|
|
|
} else {
|
|
|
|
char *ret;
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&ret, "%s/libvirt", path));
|
2012-05-03 16:36:27 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
char *virGetUserName(uid_t uid)
|
2009-07-10 10:40:04 +00:00
|
|
|
{
|
2013-07-09 23:57:48 +00:00
|
|
|
char *ret;
|
2016-06-17 11:54:52 +00:00
|
|
|
virGetUserEnt(uid, &ret, NULL, NULL, NULL, false);
|
2013-07-09 23:57:48 +00:00
|
|
|
return ret;
|
2009-07-10 10:40:04 +00:00
|
|
|
}
|
|
|
|
|
2011-12-16 00:55:19 +00:00
|
|
|
char *virGetGroupName(gid_t gid)
|
|
|
|
{
|
|
|
|
return virGetGroupEnt(gid);
|
|
|
|
}
|
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
/* Search in the password database for a user id that matches the user name
|
|
|
|
* `name`. Returns 0 on success, -1 on failure or 1 if name cannot be found.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virGetUserIDByName(const char *name, uid_t *uid)
|
2009-07-15 21:25:01 +00:00
|
|
|
{
|
2012-10-08 20:37:01 +00:00
|
|
|
char *strbuf = NULL;
|
2009-07-15 21:25:01 +00:00
|
|
|
struct passwd pwbuf;
|
|
|
|
struct passwd *pw = NULL;
|
2010-02-01 20:45:06 +00:00
|
|
|
long val = sysconf(_SC_GETPW_R_SIZE_MAX);
|
|
|
|
size_t strbuflen = val;
|
2011-05-16 21:37:15 +00:00
|
|
|
int rc;
|
2012-10-08 20:37:01 +00:00
|
|
|
int ret = -1;
|
2010-02-01 20:45:06 +00:00
|
|
|
|
2011-05-16 21:37:15 +00:00
|
|
|
/* sysconf is a hint; if it fails, fall back to a reasonable size */
|
|
|
|
if (val < 0)
|
|
|
|
strbuflen = 1024;
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(strbuf, strbuflen) < 0)
|
2012-10-08 20:37:01 +00:00
|
|
|
goto cleanup;
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2011-05-16 21:37:15 +00:00
|
|
|
while ((rc = getpwnam_r(name, &pwbuf, strbuf, strbuflen, &pw)) == ERANGE) {
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_RESIZE_N(strbuf, strbuflen, strbuflen, strbuflen) < 0)
|
2012-10-08 20:37:01 +00:00
|
|
|
goto cleanup;
|
2011-05-16 21:37:15 +00:00
|
|
|
}
|
2012-10-08 20:37:01 +00:00
|
|
|
|
|
|
|
if (!pw) {
|
2012-12-11 20:26:52 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
char buf[1024];
|
|
|
|
/* log the possible error from getpwnam_r. Unfortunately error
|
|
|
|
* reporting from this function is bad and we can't really
|
|
|
|
* rely on it, so we just report that the user wasn't found */
|
2012-12-12 13:19:03 +00:00
|
|
|
VIR_WARN("User record for user '%s' was not found: %s",
|
2012-12-11 20:26:52 +00:00
|
|
|
name, virStrerror(rc, buf, sizeof(buf)));
|
|
|
|
}
|
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
ret = 1;
|
|
|
|
goto cleanup;
|
2009-07-15 21:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*uid = pw->pw_uid;
|
2012-10-08 20:37:01 +00:00
|
|
|
ret = 0;
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2009-07-15 21:25:01 +00:00
|
|
|
VIR_FREE(strbuf);
|
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
return ret;
|
2009-07-15 21:25:01 +00:00
|
|
|
}
|
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
/* Try to match a user id based on `user`. The default behavior is to parse
|
|
|
|
* `user` first as a user name and then as a user id. However if `user`
|
|
|
|
* contains a leading '+', the rest of the string is always parsed as a uid.
|
|
|
|
*
|
|
|
|
* Returns 0 on success and -1 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virGetUserID(const char *user, uid_t *uid)
|
|
|
|
{
|
|
|
|
unsigned int uint_uid;
|
|
|
|
|
|
|
|
if (*user == '+') {
|
|
|
|
user++;
|
|
|
|
} else {
|
|
|
|
int rc = virGetUserIDByName(user, uid);
|
|
|
|
if (rc <= 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(user, NULL, 10, &uint_uid) < 0 ||
|
|
|
|
((uid_t) uint_uid) != uint_uid) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, _("Failed to parse user '%s'"),
|
|
|
|
user);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*uid = uint_uid;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
/* Search in the group database for a group id that matches the group name
|
|
|
|
* `name`. Returns 0 on success, -1 on failure or 1 if name cannot be found.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virGetGroupIDByName(const char *name, gid_t *gid)
|
2009-07-15 21:25:01 +00:00
|
|
|
{
|
2012-10-08 20:37:01 +00:00
|
|
|
char *strbuf = NULL;
|
2009-07-15 21:25:01 +00:00
|
|
|
struct group grbuf;
|
|
|
|
struct group *gr = NULL;
|
2010-02-01 20:45:06 +00:00
|
|
|
long val = sysconf(_SC_GETGR_R_SIZE_MAX);
|
|
|
|
size_t strbuflen = val;
|
2011-05-16 21:37:15 +00:00
|
|
|
int rc;
|
2012-10-08 20:37:01 +00:00
|
|
|
int ret = -1;
|
2010-02-01 20:45:06 +00:00
|
|
|
|
2011-05-16 21:37:15 +00:00
|
|
|
/* sysconf is a hint; if it fails, fall back to a reasonable size */
|
|
|
|
if (val < 0)
|
|
|
|
strbuflen = 1024;
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(strbuf, strbuflen) < 0)
|
2012-10-08 20:37:01 +00:00
|
|
|
goto cleanup;
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2011-05-16 21:37:15 +00:00
|
|
|
while ((rc = getgrnam_r(name, &grbuf, strbuf, strbuflen, &gr)) == ERANGE) {
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_RESIZE_N(strbuf, strbuflen, strbuflen, strbuflen) < 0)
|
2012-10-08 20:37:01 +00:00
|
|
|
goto cleanup;
|
2011-05-16 21:37:15 +00:00
|
|
|
}
|
2012-10-08 20:37:01 +00:00
|
|
|
|
|
|
|
if (!gr) {
|
2012-12-11 20:26:52 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
char buf[1024];
|
|
|
|
/* log the possible error from getgrnam_r. Unfortunately error
|
|
|
|
* reporting from this function is bad and we can't really
|
|
|
|
* rely on it, so we just report that the user wasn't found */
|
2012-12-12 13:19:03 +00:00
|
|
|
VIR_WARN("Group record for user '%s' was not found: %s",
|
2012-12-11 20:26:52 +00:00
|
|
|
name, virStrerror(rc, buf, sizeof(buf)));
|
|
|
|
}
|
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
ret = 1;
|
|
|
|
goto cleanup;
|
2009-07-15 21:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*gid = gr->gr_gid;
|
2012-10-08 20:37:01 +00:00
|
|
|
ret = 0;
|
2009-07-15 21:25:01 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2009-07-15 21:25:01 +00:00
|
|
|
VIR_FREE(strbuf);
|
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
return ret;
|
2009-07-15 21:25:01 +00:00
|
|
|
}
|
2010-03-17 00:06:31 +00:00
|
|
|
|
2012-10-08 20:37:01 +00:00
|
|
|
/* Try to match a group id based on `group`. The default behavior is to parse
|
|
|
|
* `group` first as a group name and then as a group id. However if `group`
|
|
|
|
* contains a leading '+', the rest of the string is always parsed as a guid.
|
|
|
|
*
|
|
|
|
* Returns 0 on success and -1 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virGetGroupID(const char *group, gid_t *gid)
|
|
|
|
{
|
|
|
|
unsigned int uint_gid;
|
|
|
|
|
|
|
|
if (*group == '+') {
|
|
|
|
group++;
|
|
|
|
} else {
|
|
|
|
int rc = virGetGroupIDByName(group, gid);
|
|
|
|
if (rc <= 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(group, NULL, 10, &uint_gid) < 0 ||
|
|
|
|
((gid_t) uint_gid) != uint_gid) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, _("Failed to parse group '%s'"),
|
|
|
|
group);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*gid = uint_gid;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2010-12-23 06:44:02 +00:00
|
|
|
|
2013-05-21 23:47:48 +00:00
|
|
|
|
2013-08-05 09:07:27 +00:00
|
|
|
/* Compute the list of primary and supplementary groups associated
|
|
|
|
* with @uid, and including @gid in the list (unless it is -1),
|
2016-06-17 13:32:58 +00:00
|
|
|
* storing a malloc'd result into @list. If uid is -1 or doesn't exist in the
|
|
|
|
* system database querying of the supplementary groups is skipped.
|
|
|
|
*
|
|
|
|
* Returns the size of the list on success, or -1 on failure with error
|
|
|
|
* reported and errno set. May not be called between fork and exec.
|
|
|
|
* */
|
2013-05-21 23:47:48 +00:00
|
|
|
int
|
|
|
|
virGetGroupList(uid_t uid, gid_t gid, gid_t **list)
|
|
|
|
{
|
2016-06-17 13:32:58 +00:00
|
|
|
int ret = 0;
|
2013-05-21 23:47:48 +00:00
|
|
|
char *user = NULL;
|
2013-08-05 09:07:27 +00:00
|
|
|
gid_t primary;
|
2013-05-21 23:47:48 +00:00
|
|
|
|
|
|
|
*list = NULL;
|
|
|
|
|
2016-06-17 13:32:58 +00:00
|
|
|
/* invalid users have no supplementary groups */
|
|
|
|
if (uid != (uid_t)-1 &&
|
|
|
|
virGetUserEnt(uid, &user, &primary, NULL, NULL, true) >= 0) {
|
|
|
|
if ((ret = mgetgroups(user, primary, list)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot get group list for '%s'"), user);
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-08-05 09:07:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gid != (gid_t)-1) {
|
2013-08-07 22:44:58 +00:00
|
|
|
size_t i;
|
2013-08-05 09:07:27 +00:00
|
|
|
|
2013-08-07 22:44:58 +00:00
|
|
|
for (i = 0; i < ret; i++) {
|
2013-08-05 09:07:27 +00:00
|
|
|
if ((*list)[i] == gid)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-08-07 22:44:58 +00:00
|
|
|
if (VIR_APPEND_ELEMENT(*list, i, gid) < 0) {
|
2013-08-05 09:07:27 +00:00
|
|
|
ret = -1;
|
|
|
|
VIR_FREE(*list);
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
2013-08-07 22:44:58 +00:00
|
|
|
ret = i;
|
2013-08-05 09:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-05-21 23:47:48 +00:00
|
|
|
VIR_FREE(user);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
/* Set the real and effective uid and gid to the given values, as well
|
|
|
|
* as all the supplementary groups, so that the process has all the
|
|
|
|
* assumed group membership of that uid. Return 0 on success, -1 on
|
|
|
|
* failure (the original system error remains in errno).
|
2010-12-23 06:44:02 +00:00
|
|
|
*/
|
|
|
|
int
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
virSetUIDGID(uid_t uid, gid_t gid, gid_t *groups ATTRIBUTE_UNUSED,
|
|
|
|
int ngroups ATTRIBUTE_UNUSED)
|
2010-12-23 06:44:02 +00:00
|
|
|
{
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
if (gid != (gid_t)-1 && setregid(gid, gid) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot change to '%u' group"),
|
|
|
|
(unsigned int) gid);
|
|
|
|
return -1;
|
2010-12-23 06:44:02 +00:00
|
|
|
}
|
|
|
|
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
# if HAVE_SETGROUPS
|
2015-06-23 11:48:42 +00:00
|
|
|
if (gid != (gid_t)-1 && setgroups(ngroups, groups) < 0) {
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("cannot set supplemental groups"));
|
|
|
|
return -1;
|
|
|
|
}
|
2010-12-23 06:44:02 +00:00
|
|
|
# endif
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
|
|
|
|
if (uid != (uid_t)-1 && setreuid(uid, uid) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot change to uid to '%u'"),
|
|
|
|
(unsigned int) uid);
|
|
|
|
return -1;
|
2010-12-23 06:44:02 +00:00
|
|
|
}
|
2012-08-28 14:51:05 +00:00
|
|
|
|
2010-12-23 06:44:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
#else /* ! HAVE_GETPWUID_R */
|
|
|
|
|
2013-07-29 14:04:35 +00:00
|
|
|
int
|
|
|
|
virGetGroupList(uid_t uid ATTRIBUTE_UNUSED, gid_t gid ATTRIBUTE_UNUSED,
|
|
|
|
gid_t **list)
|
|
|
|
{
|
|
|
|
*list = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
# ifdef WIN32
|
|
|
|
/* These methods are adapted from GLib2 under terms of LGPLv2+ */
|
|
|
|
static int
|
|
|
|
virGetWin32SpecialFolder(int csidl, char **path)
|
|
|
|
{
|
|
|
|
char buf[MAX_PATH+1];
|
|
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
*path = NULL;
|
|
|
|
|
|
|
|
if (SHGetSpecialFolderLocation(NULL, csidl, &pidl) == S_OK) {
|
2013-05-24 07:19:51 +00:00
|
|
|
if (SHGetPathFromIDList(pidl, buf) && VIR_STRDUP(*path, buf) < 0)
|
|
|
|
ret = -1;
|
2012-05-24 13:37:54 +00:00
|
|
|
CoTaskMemFree(pidl);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virGetWin32DirectoryRoot(char **path)
|
|
|
|
{
|
|
|
|
char windowsdir[MAX_PATH];
|
|
|
|
|
|
|
|
*path = NULL;
|
|
|
|
|
2014-09-03 22:24:43 +00:00
|
|
|
if (GetWindowsDirectory(windowsdir, ARRAY_CARDINALITY(windowsdir))) {
|
2012-05-24 13:37:54 +00:00
|
|
|
const char *tmp;
|
|
|
|
/* Usually X:\Windows, but in terminal server environments
|
|
|
|
* might be an UNC path, AFAIK.
|
|
|
|
*/
|
|
|
|
tmp = virFileSkipRoot(windowsdir);
|
|
|
|
if (VIR_FILE_IS_DIR_SEPARATOR(tmp[-1]) &&
|
|
|
|
tmp[-2] != ':')
|
|
|
|
tmp--;
|
|
|
|
|
|
|
|
windowsdir[tmp - windowsdir] = '\0';
|
|
|
|
} else {
|
|
|
|
strcpy(windowsdir, "C:\\");
|
|
|
|
}
|
|
|
|
|
2013-05-24 07:19:51 +00:00
|
|
|
return VIR_STRDUP(*path, windowsdir) < 0 ? -1 : 0;
|
2012-05-24 13:37:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
2013-07-30 19:55:44 +00:00
|
|
|
virGetUserDirectoryByUID(uid_t uid ATTRIBUTE_UNUSED)
|
2012-05-24 13:37:54 +00:00
|
|
|
{
|
2013-07-30 19:55:44 +00:00
|
|
|
/* Since Windows lacks setuid binaries, and since we already fake
|
|
|
|
* geteuid(), we can safely assume that this is only called when
|
|
|
|
* querying about the current user */
|
2012-05-24 13:37:54 +00:00
|
|
|
const char *dir;
|
|
|
|
char *ret;
|
|
|
|
|
2013-10-09 10:18:15 +00:00
|
|
|
dir = virGetEnvBlockSUID("HOME");
|
2012-05-24 13:37:54 +00:00
|
|
|
|
|
|
|
/* Only believe HOME if it is an absolute path and exists */
|
|
|
|
if (dir) {
|
|
|
|
if (!virFileIsAbsPath(dir) ||
|
|
|
|
!virFileExists(dir))
|
|
|
|
dir = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In case HOME is Unix-style (it happens), convert it to
|
|
|
|
* Windows style.
|
|
|
|
*/
|
|
|
|
if (dir) {
|
|
|
|
char *p;
|
2012-10-17 09:23:12 +00:00
|
|
|
while ((p = strchr(dir, '/')) != NULL)
|
2012-05-24 13:37:54 +00:00
|
|
|
*p = '\\';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dir)
|
|
|
|
/* USERPROFILE is probably the closest equivalent to $HOME? */
|
2013-10-09 10:18:15 +00:00
|
|
|
dir = virGetEnvBlockSUID("USERPROFILE");
|
2012-05-24 13:37:54 +00:00
|
|
|
|
2013-05-24 07:19:51 +00:00
|
|
|
if (VIR_STRDUP(ret, dir) < 0)
|
|
|
|
return NULL;
|
2012-05-24 13:37:54 +00:00
|
|
|
|
|
|
|
if (!ret &&
|
|
|
|
virGetWin32SpecialFolder(CSIDL_PROFILE, &ret) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!ret &&
|
|
|
|
virGetWin32DirectoryRoot(&ret) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!ret) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to determine home directory"));
|
2012-05-24 13:37:54 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-04-12 17:11:20 +00:00
|
|
|
char *
|
|
|
|
virGetUserShell(uid_t uid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserShell is not available"));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
char *
|
|
|
|
virGetUserConfigDirectory(void)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
if (virGetWin32SpecialFolder(CSIDL_LOCAL_APPDATA, &ret) < 0)
|
|
|
|
return NULL;
|
2010-03-17 00:06:31 +00:00
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
if (!ret) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to determine config directory"));
|
2012-05-24 13:37:54 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
virGetUserCacheDirectory(void)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
if (virGetWin32SpecialFolder(CSIDL_INTERNET_CACHE, &ret) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!ret) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to determine config directory"));
|
2012-05-24 13:37:54 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
virGetUserRuntimeDirectory(void)
|
|
|
|
{
|
|
|
|
return virGetUserCacheDirectory();
|
|
|
|
}
|
2013-07-29 14:04:35 +00:00
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
# else /* !HAVE_GETPWUID_R && !WIN32 */
|
2010-03-17 00:06:31 +00:00
|
|
|
char *
|
2013-07-30 19:55:44 +00:00
|
|
|
virGetUserDirectoryByUID(uid_t uid ATTRIBUTE_UNUSED)
|
2010-03-17 00:06:31 +00:00
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserDirectory is not available"));
|
2010-03-17 00:06:31 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-12 17:11:20 +00:00
|
|
|
char *
|
|
|
|
virGetUserShell(uid_t uid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserShell is not available"));
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-05-24 13:37:54 +00:00
|
|
|
char *
|
|
|
|
virGetUserConfigDirectory(void)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserConfigDirectory is not available"));
|
2012-05-24 13:37:54 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
virGetUserCacheDirectory(void)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserCacheDirectory is not available"));
|
2012-05-24 13:37:54 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
virGetUserRuntimeDirectory(void)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserRuntimeDirectory is not available"));
|
2012-05-24 13:37:54 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
# endif /* ! HAVE_GETPWUID_R && ! WIN32 */
|
|
|
|
|
2010-03-17 00:06:31 +00:00
|
|
|
char *
|
|
|
|
virGetUserName(uid_t uid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserName is not available"));
|
2010-03-17 00:06:31 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int virGetUserID(const char *name ATTRIBUTE_UNUSED,
|
|
|
|
uid_t *uid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetUserID is not available"));
|
2010-03-17 00:06:31 +00:00
|
|
|
|
2015-06-08 07:32:24 +00:00
|
|
|
return -1;
|
2010-03-17 00:06:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int virGetGroupID(const char *name ATTRIBUTE_UNUSED,
|
|
|
|
gid_t *gid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetGroupID is not available"));
|
2010-03-17 00:06:31 +00:00
|
|
|
|
2015-06-08 07:32:24 +00:00
|
|
|
return -1;
|
2010-03-17 00:06:31 +00:00
|
|
|
}
|
2010-12-23 06:44:02 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
virSetUIDGID(uid_t uid ATTRIBUTE_UNUSED,
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
gid_t gid ATTRIBUTE_UNUSED,
|
|
|
|
gid_t *groups ATTRIBUTE_UNUSED,
|
|
|
|
int ngroups ATTRIBUTE_UNUSED)
|
2010-12-23 06:44:02 +00:00
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virSetUIDGID is not available"));
|
2010-12-23 06:44:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-01-25 18:54:43 +00:00
|
|
|
|
|
|
|
char *
|
|
|
|
virGetGroupName(gid_t gid ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("virGetGroupName is not available"));
|
2012-01-25 18:54:43 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-03-17 00:06:31 +00:00
|
|
|
#endif /* HAVE_GETPWUID_R */
|
Support configuration of huge pages in guests
Add option to domain XML for
<memoryBacking>
<hugepages/>
</memoryBacking>
* configure.in: Add check for mntent.h
* qemud/libvirtd_qemu.aug, qemud/test_libvirtd_qemu.aug, src/qemu.conf
Add 'hugetlbfs_mount' config parameter
* src/qemu_conf.c, src/qemu_conf.h: Check for -mem-path flag in QEMU,
and pass it when hugepages are requested.
Load hugetlbfs_mount config parameter, search for mount if not given.
* src/qemu_driver.c: Free hugetlbfs_mount/path parameter in driver shutdown.
Create directory for QEMU hugepage usage, chowning if required.
* docs/formatdomain.html.in: Document memoryBacking/hugepages elements
* docs/schemas/domain.rng: Add memoryBacking/hugepages elements to schema
* src/util.c, src/util.h, src/libvirt_private.syms: Add virFileFindMountPoint
helper API
* tests/qemuhelptest.c: Add -mem-path constants
* tests/qemuxml2argvtest.c, tests/qemuxml2xmltest.c: Add tests for hugepage
handling
* tests/qemuxml2argvdata/qemuxml2argv-hugepages.xml,
tests/qemuxml2argvdata/qemuxml2argv-hugepages.args: Data files for
hugepage tests
2009-08-25 14:05:18 +00:00
|
|
|
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
#if WITH_CAPNG
|
|
|
|
/* Set the real and effective uid and gid to the given values, while
|
|
|
|
* maintaining the capabilities indicated by bits in @capBits. Return
|
|
|
|
* 0 on success, -1 on failure (the original system error remains in
|
|
|
|
* errno).
|
|
|
|
*/
|
|
|
|
int
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
|
|
|
|
unsigned long long capBits, bool clearExistingCaps)
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
|
|
|
int capng_ret, ret = -1;
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
bool need_setgid = false, need_setuid = false;
|
2013-03-25 14:25:28 +00:00
|
|
|
bool need_setpcap = false;
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
|
2013-03-13 19:26:35 +00:00
|
|
|
/* First drop all caps (unless the requested uid is "unchanged" or
|
|
|
|
* root and clearExistingCaps wasn't requested), then add back
|
|
|
|
* those in capBits + the extra ones we need to change uid/gid and
|
|
|
|
* change the capabilities bounding set.
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
*/
|
|
|
|
|
2013-03-14 22:28:29 +00:00
|
|
|
if (clearExistingCaps || (uid != (uid_t)-1 && uid != 0))
|
2013-03-13 19:26:35 +00:00
|
|
|
capng_clear(CAPNG_SELECT_BOTH);
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
for (i = 0; i <= CAP_LAST_CAP; i++) {
|
|
|
|
if (capBits & (1ULL << i)) {
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
capng_update(CAPNG_ADD,
|
|
|
|
CAPNG_EFFECTIVE|CAPNG_INHERITABLE|
|
|
|
|
CAPNG_PERMITTED|CAPNG_BOUNDING_SET,
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
i);
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gid != (gid_t)-1 &&
|
|
|
|
!capng_have_capability(CAPNG_EFFECTIVE, CAP_SETGID)) {
|
|
|
|
need_setgid = true;
|
|
|
|
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETGID);
|
|
|
|
}
|
|
|
|
if (uid != (uid_t)-1 &&
|
|
|
|
!capng_have_capability(CAPNG_EFFECTIVE, CAP_SETUID)) {
|
|
|
|
need_setuid = true;
|
|
|
|
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETUID);
|
|
|
|
}
|
|
|
|
# ifdef PR_CAPBSET_DROP
|
|
|
|
/* If newer kernel, we need also need setpcap to change the bounding set */
|
|
|
|
if ((capBits || need_setgid || need_setuid) &&
|
|
|
|
!capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
|
|
|
|
need_setpcap = true;
|
|
|
|
}
|
|
|
|
if (need_setpcap)
|
|
|
|
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP);
|
|
|
|
# endif
|
|
|
|
|
|
|
|
/* Tell system we want to keep caps across uid change */
|
2013-03-25 14:25:28 +00:00
|
|
|
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("prctl failed to set KEEPCAPS"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Change to the temp capabilities */
|
2013-03-25 14:25:28 +00:00
|
|
|
if ((capng_ret = capng_apply(CAPNG_SELECT_CAPS)) < 0) {
|
2013-03-25 14:25:29 +00:00
|
|
|
/* Failed. If we are running unprivileged, and the arguments make sense
|
|
|
|
* for this scenario, assume we're starting some kind of setuid helper:
|
|
|
|
* do not set any of capBits in the permitted or effective sets, and let
|
|
|
|
* the program get them on its own.
|
|
|
|
*
|
|
|
|
* (Too bad we cannot restrict the bounding set to the capabilities we
|
|
|
|
* would like the helper to have!).
|
|
|
|
*/
|
|
|
|
if (getuid() > 0 && clearExistingCaps && !need_setuid && !need_setgid) {
|
|
|
|
capng_clear(CAPNG_SELECT_CAPS);
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot apply process capabilities %d"), capng_ret);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
}
|
|
|
|
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
if (virSetUIDGID(uid, gid, groups, ngroups) < 0)
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Tell it we are done keeping capabilities */
|
2013-03-25 14:25:28 +00:00
|
|
|
if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0)) {
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("prctl failed to reset KEEPCAPS"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-03-25 14:25:28 +00:00
|
|
|
/* Set bounding set while we have CAP_SETPCAP. Unfortunately we cannot
|
|
|
|
* do this if we failed to get the capability above, so ignore the
|
|
|
|
* return value.
|
|
|
|
*/
|
|
|
|
capng_apply(CAPNG_SELECT_BOUNDS);
|
|
|
|
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
/* Drop the caps that allow setuid/gid (unless they were requested) */
|
|
|
|
if (need_setgid)
|
|
|
|
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETGID);
|
|
|
|
if (need_setuid)
|
|
|
|
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETUID);
|
|
|
|
/* Throw away CAP_SETPCAP so no more changes */
|
|
|
|
if (need_setpcap)
|
|
|
|
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP);
|
|
|
|
|
2013-03-25 14:25:28 +00:00
|
|
|
if (((capng_ret = capng_apply(CAPNG_SELECT_CAPS)) < 0)) {
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot apply process capabilities %d"), capng_ret);
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* On platforms without libcapng, the capabilities setting is treated
|
|
|
|
* as a NOP.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
|
2013-03-13 19:26:35 +00:00
|
|
|
unsigned long long capBits ATTRIBUTE_UNUSED,
|
|
|
|
bool clearExistingCaps ATTRIBUTE_UNUSED)
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
{
|
util: make virSetUIDGID async-signal-safe
https://bugzilla.redhat.com/show_bug.cgi?id=964358
POSIX states that multi-threaded apps should not use functions
that are not async-signal-safe between fork and exec, yet we
were using getpwuid_r and initgroups. Although rare, it is
possible to hit deadlock in the child, when it tries to grab
a mutex that was already held by another thread in the parent.
I actually hit this deadlock when testing multiple domains
being started in parallel with a command hook, with the following
backtrace in the child:
Thread 1 (Thread 0x7fd56bbf2700 (LWP 3212)):
#0 __lll_lock_wait ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007fd5761e7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007fd5761e7257 in __pthread_mutex_lock (mutex=0x7fd56be00360)
at pthread_mutex_lock.c:61
#3 0x00007fd56bbf9fc5 in _nss_files_getpwuid_r (uid=0, result=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, errnop=0x7fd56bbf25b8)
at nss_files/files-pwd.c:40
#4 0x00007fd575aeff1d in __getpwuid_r (uid=0, resbuf=0x7fd56bbf0c70,
buffer=0x7fd55c2a65f0 "", buflen=1024, result=0x7fd56bbf0cb0)
at ../nss/getXXbyYY_r.c:253
#5 0x00007fd578aebafc in virSetUIDGID (uid=0, gid=0) at util/virutil.c:1031
#6 0x00007fd578aebf43 in virSetUIDGIDWithCaps (uid=0, gid=0, capBits=0,
clearExistingCaps=true) at util/virutil.c:1388
#7 0x00007fd578a9a20b in virExec (cmd=0x7fd55c231f10) at util/vircommand.c:654
#8 0x00007fd578a9dfa2 in virCommandRunAsync (cmd=0x7fd55c231f10, pid=0x0)
at util/vircommand.c:2247
#9 0x00007fd578a9d74e in virCommandRun (cmd=0x7fd55c231f10, exitstatus=0x0)
at util/vircommand.c:2100
#10 0x00007fd56326fde5 in qemuProcessStart (conn=0x7fd53c000df0,
driver=0x7fd55c0dc4f0, vm=0x7fd54800b100, migrateFrom=0x0, stdin_fd=-1,
stdin_path=0x0, snapshot=0x0, vmop=VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
flags=1) at qemu/qemu_process.c:3694
...
The solution is to split the work of getpwuid_r/initgroups into the
unsafe portions (getgrouplist, called pre-fork) and safe portions
(setgroups, called post-fork).
* src/util/virutil.h (virSetUIDGID, virSetUIDGIDWithCaps): Adjust
signature.
* src/util/virutil.c (virSetUIDGID): Add parameters.
(virSetUIDGIDWithCaps): Adjust clients.
* src/util/vircommand.c (virExec): Likewise.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* src/security/security_dac.c (virSecurityDACSetProcessLabel):
Likewise.
* src/lxc/lxc_container.c (lxcContainerSetID): Likewise.
* configure.ac (AC_CHECK_FUNCS_ONCE): Check for setgroups, not
initgroups.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-05-22 02:59:10 +00:00
|
|
|
return virSetUIDGID(uid, gid, groups, ngroups);
|
util: virSetUIDGIDWithCaps - change uid while keeping caps
Normally when a process' uid is changed to non-0, all the capabilities
bits are cleared, even those explicitly set with calls to
capng_update()/capng_apply() made immediately before setuid. And
*after* the process' uid has been changed, it no longer has the
necessary privileges to add capabilities back to the process.
In order to set a non-0 uid while still maintaining any capabilities
bits, it is necessary to either call capng_change_id() (which
unfortunately doesn't currently call initgroups to setup auxiliary
group membership), or to perform the small amount of calisthenics
contained in the new utility function virSetUIDGIDWithCaps().
Another very important difference between the capabilities
setting/clearing in virSetUIDGIDWithCaps() and virCommand's
virSetCapabilities() (which it will replace in the next patch) is that
the new function properly clears the capabilities bounding set, so it
will not be possible for a child process to set any new
capabilities.
A short description of what is done by virSetUIDGIDWithCaps():
1) clear all capabilities then set all those desired by the caller (in
capBits) plus CAP_SETGID, CAP_SETUID, and CAP_SETPCAP (which is needed
to change the capabilities bounding set).
2) call prctl(), telling it that we want to maintain current
capabilities across an upcoming setuid().
3) switch to the new uid/gid
4) again call prctl(), telling it we will no longer want capabilities
maintained if this process does another setuid().
5) clear the capabilities that we added to allow us to
setuid/setgid/change the bounding set (unless they were also requested
by the caller via the virCommand API).
Because the modification/maintaining of capabilities is intermingled
with setting the uid, this is necessarily done in a single function,
rather than having two independent functions.
Note that, due to the way that effective capabilities are computed (at
time of execve) for a process that has uid != 0, the *file*
capabilities of the binary being executed must also have the desired
capabilities bit(s) set (see "man 7 capabilities"). This can be done
with the "filecap" command. (e.g. "filecap /usr/bin/qemu-kvm sys_rawio").
2013-02-05 15:30:32 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Support configuration of huge pages in guests
Add option to domain XML for
<memoryBacking>
<hugepages/>
</memoryBacking>
* configure.in: Add check for mntent.h
* qemud/libvirtd_qemu.aug, qemud/test_libvirtd_qemu.aug, src/qemu.conf
Add 'hugetlbfs_mount' config parameter
* src/qemu_conf.c, src/qemu_conf.h: Check for -mem-path flag in QEMU,
and pass it when hugepages are requested.
Load hugetlbfs_mount config parameter, search for mount if not given.
* src/qemu_driver.c: Free hugetlbfs_mount/path parameter in driver shutdown.
Create directory for QEMU hugepage usage, chowning if required.
* docs/formatdomain.html.in: Document memoryBacking/hugepages elements
* docs/schemas/domain.rng: Add memoryBacking/hugepages elements to schema
* src/util.c, src/util.h, src/libvirt_private.syms: Add virFileFindMountPoint
helper API
* tests/qemuhelptest.c: Add -mem-path constants
* tests/qemuxml2argvtest.c, tests/qemuxml2xmltest.c: Add tests for hugepage
handling
* tests/qemuxml2argvdata/qemuxml2argv-hugepages.xml,
tests/qemuxml2argvdata/qemuxml2argv-hugepages.args: Data files for
hugepage tests
2009-08-25 14:05:18 +00:00
|
|
|
|
2010-11-08 16:32:02 +00:00
|
|
|
#if defined(UDEVADM) || defined(UDEVSETTLE)
|
2010-02-04 22:41:52 +00:00
|
|
|
void virFileWaitForDevices(void)
|
2009-09-10 13:21:10 +00:00
|
|
|
{
|
2010-11-08 16:32:02 +00:00
|
|
|
# ifdef UDEVADM
|
2009-09-10 13:21:10 +00:00
|
|
|
const char *const settleprog[] = { UDEVADM, "settle", NULL };
|
2010-11-08 16:32:02 +00:00
|
|
|
# else
|
2009-09-10 13:21:10 +00:00
|
|
|
const char *const settleprog[] = { UDEVSETTLE, NULL };
|
2010-11-08 16:32:02 +00:00
|
|
|
# endif
|
2009-09-10 13:21:10 +00:00
|
|
|
int exitstatus;
|
|
|
|
|
|
|
|
if (access(settleprog[0], X_OK) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: we ignore errors here; this is just to make sure that any device
|
|
|
|
* nodes that are being created finish before we try to scan them.
|
|
|
|
* If this fails for any reason, we still have the backup of polling for
|
|
|
|
* 5 seconds for device nodes.
|
|
|
|
*/
|
2014-09-03 22:24:43 +00:00
|
|
|
ignore_value(virRun(settleprog, &exitstatus));
|
2009-09-10 13:21:10 +00:00
|
|
|
}
|
2010-11-08 16:32:02 +00:00
|
|
|
#else
|
2014-03-18 08:14:35 +00:00
|
|
|
void virFileWaitForDevices(void)
|
|
|
|
{}
|
2009-09-10 13:21:10 +00:00
|
|
|
#endif
|
2009-11-12 21:48:24 +00:00
|
|
|
|
2011-02-18 19:00:47 +00:00
|
|
|
#if HAVE_LIBDEVMAPPER_H
|
2011-02-17 07:29:07 +00:00
|
|
|
bool
|
2011-09-16 12:05:58 +00:00
|
|
|
virIsDevMapperDevice(const char *dev_name)
|
2011-02-17 07:29:07 +00:00
|
|
|
{
|
|
|
|
struct stat buf;
|
|
|
|
|
2011-09-16 12:05:58 +00:00
|
|
|
if (!stat(dev_name, &buf) &&
|
2011-02-17 07:29:07 +00:00
|
|
|
S_ISBLK(buf.st_mode) &&
|
|
|
|
dm_is_dm_major(major(buf.st_rdev)))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2011-02-18 19:00:47 +00:00
|
|
|
#else
|
2011-09-16 12:05:58 +00:00
|
|
|
bool virIsDevMapperDevice(const char *dev_name ATTRIBUTE_UNUSED)
|
2011-02-18 19:00:47 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
2012-09-11 08:57:02 +00:00
|
|
|
|
|
|
|
bool
|
2014-03-18 08:14:35 +00:00
|
|
|
virValidateWWN(const char *wwn)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
qemu: Allow the disk wwn to have "0x" prefix
The recent qemu requires "0x" prefix for the disk wwn, this patch
changes virValidateWWN to allow the prefix, and prepend "0x" if
it's not specified. E.g.
qemu-kvm: -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\
drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=6000c60016ea71ad:
Property 'scsi-hd.wwn' doesn't take value '6000c60016ea71ad'
Though it's a qemu regression, but it's nice to allow the prefix,
and doesn't hurt for us to always output "0x".
2013-04-17 13:23:44 +00:00
|
|
|
const char *p = wwn;
|
2012-09-11 08:57:02 +00:00
|
|
|
|
2014-11-13 14:28:18 +00:00
|
|
|
if (STRPREFIX(wwn, "0x"))
|
qemu: Allow the disk wwn to have "0x" prefix
The recent qemu requires "0x" prefix for the disk wwn, this patch
changes virValidateWWN to allow the prefix, and prepend "0x" if
it's not specified. E.g.
qemu-kvm: -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\
drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=6000c60016ea71ad:
Property 'scsi-hd.wwn' doesn't take value '6000c60016ea71ad'
Though it's a qemu regression, but it's nice to allow the prefix,
and doesn't hurt for us to always output "0x".
2013-04-17 13:23:44 +00:00
|
|
|
p += 2;
|
|
|
|
|
|
|
|
for (i = 0; p[i]; i++) {
|
|
|
|
if (!c_isxdigit(p[i]))
|
2012-09-11 08:57:02 +00:00
|
|
|
break;
|
qemu: Allow the disk wwn to have "0x" prefix
The recent qemu requires "0x" prefix for the disk wwn, this patch
changes virValidateWWN to allow the prefix, and prepend "0x" if
it's not specified. E.g.
qemu-kvm: -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\
drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=6000c60016ea71ad:
Property 'scsi-hd.wwn' doesn't take value '6000c60016ea71ad'
Though it's a qemu regression, but it's nice to allow the prefix,
and doesn't hurt for us to always output "0x".
2013-04-17 13:23:44 +00:00
|
|
|
}
|
2012-09-11 08:57:02 +00:00
|
|
|
|
qemu: Allow the disk wwn to have "0x" prefix
The recent qemu requires "0x" prefix for the disk wwn, this patch
changes virValidateWWN to allow the prefix, and prepend "0x" if
it's not specified. E.g.
qemu-kvm: -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,\
drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,wwn=6000c60016ea71ad:
Property 'scsi-hd.wwn' doesn't take value '6000c60016ea71ad'
Though it's a qemu regression, but it's nice to allow the prefix,
and doesn't hurt for us to always output "0x".
2013-04-17 13:23:44 +00:00
|
|
|
if (i != 16 || p[i]) {
|
2014-11-11 02:12:20 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Malformed wwn: %s"), wwn);
|
2012-09-11 08:57:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2012-12-06 10:23:02 +00:00
|
|
|
|
2013-01-02 14:37:06 +00:00
|
|
|
#if defined(major) && defined(minor)
|
|
|
|
int
|
|
|
|
virGetDeviceID(const char *path, int *maj, int *min)
|
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
|
2013-01-09 23:34:15 +00:00
|
|
|
if (stat(path, &sb) < 0)
|
2013-01-02 14:37:06 +00:00
|
|
|
return -errno;
|
|
|
|
|
2013-01-09 23:34:15 +00:00
|
|
|
if (!S_ISBLK(sb.st_mode))
|
2013-01-02 14:37:06 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (maj)
|
|
|
|
*maj = major(sb.st_rdev);
|
|
|
|
if (min)
|
|
|
|
*min = minor(sb.st_rdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int
|
2013-02-15 22:01:44 +00:00
|
|
|
virGetDeviceID(const char *path ATTRIBUTE_UNUSED,
|
|
|
|
int *maj ATTRIBUTE_UNUSED,
|
|
|
|
int *min ATTRIBUTE_UNUSED)
|
2013-01-02 14:37:06 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SYSFS_DEV_BLOCK_PATH "/sys/dev/block"
|
|
|
|
|
2013-01-02 14:37:10 +00:00
|
|
|
char *
|
2013-01-02 14:37:06 +00:00
|
|
|
virGetUnprivSGIOSysfsPath(const char *path,
|
|
|
|
const char *sysfs_dir)
|
|
|
|
{
|
|
|
|
int maj, min;
|
|
|
|
char *sysfs_path = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ((rc = virGetDeviceID(path, &maj, &min)) < 0) {
|
|
|
|
virReportSystemError(-rc,
|
|
|
|
_("Unable to get device ID '%s'"),
|
|
|
|
path);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
ignore_value(virAsprintf(&sysfs_path, "%s/%d:%d/queue/unpriv_sgio",
|
|
|
|
sysfs_dir ? sysfs_dir : SYSFS_DEV_BLOCK_PATH,
|
|
|
|
maj, min));
|
2013-01-02 14:37:06 +00:00
|
|
|
return sysfs_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virSetDeviceUnprivSGIO(const char *path,
|
|
|
|
const char *sysfs_dir,
|
|
|
|
int unpriv_sgio)
|
|
|
|
{
|
|
|
|
char *sysfs_path = NULL;
|
|
|
|
char *val = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!(sysfs_path = virGetUnprivSGIOSysfsPath(path, sysfs_dir)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!virFileExists(sysfs_path)) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("unpriv_sgio is not supported by this kernel"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-07-04 10:17:18 +00:00
|
|
|
if (virAsprintf(&val, "%d", unpriv_sgio) < 0)
|
2013-01-02 14:37:06 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((rc = virFileWriteStr(sysfs_path, val, 0)) < 0) {
|
|
|
|
virReportSystemError(-rc, _("failed to set %s"), sysfs_path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-01-02 14:37:06 +00:00
|
|
|
VIR_FREE(sysfs_path);
|
|
|
|
VIR_FREE(val);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virGetDeviceUnprivSGIO(const char *path,
|
|
|
|
const char *sysfs_dir,
|
|
|
|
int *unpriv_sgio)
|
|
|
|
{
|
|
|
|
char *sysfs_path = NULL;
|
|
|
|
char *buf = NULL;
|
|
|
|
char *tmp = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (!(sysfs_path = virGetUnprivSGIOSysfsPath(path, sysfs_dir)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!virFileExists(sysfs_path)) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("unpriv_sgio is not supported by this kernel"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileReadAll(sysfs_path, 1024, &buf) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((tmp = strchr(buf, '\n')))
|
|
|
|
*tmp = '\0';
|
|
|
|
|
|
|
|
if (virStrToLong_i(buf, NULL, 10, unpriv_sgio) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to parse value of %s"), sysfs_path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-01-02 14:37:06 +00:00
|
|
|
VIR_FREE(sysfs_path);
|
|
|
|
VIR_FREE(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-01-07 17:05:29 +00:00
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
# define SYSFS_FC_HOST_PATH "/sys/class/fc_host/"
|
2013-01-07 17:05:31 +00:00
|
|
|
# define SYSFS_SCSI_HOST_PATH "/sys/class/scsi_host/"
|
2013-01-07 17:05:29 +00:00
|
|
|
|
2014-06-10 15:09:20 +00:00
|
|
|
/* virReadSCSIUniqueId:
|
|
|
|
* @sysfs_prefix: "scsi_host" sysfs path, defaults to SYSFS_SCSI_HOST_PATH
|
|
|
|
* @host: Host number, E.g. 5 of "scsi_host/host5"
|
|
|
|
* @result: Return the entry value as an unsigned int
|
|
|
|
*
|
|
|
|
* Read the value of the "scsi_host" unique_id file.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, and @result is filled with the unique_id value
|
|
|
|
* Otherwise returns -1
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virReadSCSIUniqueId(const char *sysfs_prefix,
|
|
|
|
int host,
|
|
|
|
int *result)
|
|
|
|
{
|
|
|
|
char *sysfs_path = NULL;
|
|
|
|
char *p = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *buf = NULL;
|
|
|
|
int unique_id;
|
|
|
|
|
|
|
|
if (virAsprintf(&sysfs_path, "%s/host%d/unique_id",
|
|
|
|
sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_HOST_PATH,
|
|
|
|
host) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileReadAll(sysfs_path, 1024, &buf) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((p = strchr(buf, '\n')))
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
if (virStrToLong_i(buf, NULL, 10, &unique_id) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to parse unique_id: %s"), buf);
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = unique_id;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(sysfs_path);
|
|
|
|
VIR_FREE(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-10 11:47:12 +00:00
|
|
|
/* virFindSCSIHostByPCI:
|
|
|
|
* @sysfs_prefix: "scsi_host" sysfs path, defaults to SYSFS_SCSI_HOST_PATH
|
|
|
|
* @parentaddr: string of the PCI address "scsi_host" device to be found
|
|
|
|
* @unique_id: unique_id value of the to be found "scsi_host" device
|
|
|
|
* @result: Return the host# of the matching "scsi_host" device
|
|
|
|
*
|
|
|
|
* Iterate over the SYSFS_SCSI_HOST_PATH entries looking for a matching
|
|
|
|
* PCI Address in the expected format (dddd:bb:ss.f, where 'dddd' is the
|
|
|
|
* 'domain' value, 'bb' is the 'bus' value, 'ss' is the 'slot' value, and
|
|
|
|
* 'f' is the 'function' value from the PCI address) with a unique_id file
|
|
|
|
* entry having the value expected. Unlike virReadSCSIUniqueId() we don't
|
|
|
|
* have a host number yet and that's what we're looking for.
|
|
|
|
*
|
|
|
|
* Returns the host name of the "scsi_host" which must be freed by the caller,
|
|
|
|
* or NULL on failure
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
virFindSCSIHostByPCI(const char *sysfs_prefix,
|
|
|
|
const char *parentaddr,
|
|
|
|
unsigned int unique_id)
|
|
|
|
{
|
|
|
|
const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_HOST_PATH;
|
|
|
|
struct dirent *entry = NULL;
|
|
|
|
DIR *dir = NULL;
|
|
|
|
char *host_link = NULL;
|
|
|
|
char *host_path = NULL;
|
|
|
|
char *p = NULL;
|
|
|
|
char *ret = NULL;
|
|
|
|
char *buf = NULL;
|
|
|
|
char *unique_path = NULL;
|
|
|
|
unsigned int read_unique_id;
|
|
|
|
|
2016-06-21 14:34:08 +00:00
|
|
|
if (virDirOpen(&dir, prefix) < 0)
|
2014-11-24 16:37:13 +00:00
|
|
|
return NULL;
|
2014-06-10 11:47:12 +00:00
|
|
|
|
|
|
|
while (virDirRead(dir, &entry, prefix) > 0) {
|
2016-06-21 15:29:47 +00:00
|
|
|
if (!virFileIsLink(entry->d_name))
|
2014-06-10 11:47:12 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virAsprintf(&host_link, "%s/%s", prefix, entry->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileResolveLink(host_link, &host_path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!strstr(host_path, parentaddr)) {
|
|
|
|
VIR_FREE(host_link);
|
|
|
|
VIR_FREE(host_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VIR_FREE(host_link);
|
|
|
|
VIR_FREE(host_path);
|
|
|
|
|
|
|
|
if (virAsprintf(&unique_path, "%s/%s/unique_id", prefix,
|
|
|
|
entry->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!virFileExists(unique_path)) {
|
|
|
|
VIR_FREE(unique_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileReadAll(unique_path, 1024, &buf) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((p = strchr(buf, '\n')))
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
if (virStrToLong_ui(buf, NULL, 10, &read_unique_id) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2015-04-27 06:41:41 +00:00
|
|
|
VIR_FREE(buf);
|
|
|
|
|
2014-06-10 11:47:12 +00:00
|
|
|
if (read_unique_id != unique_id) {
|
|
|
|
VIR_FREE(unique_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore_value(VIR_STRDUP(ret, entry->d_name));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2016-06-21 10:40:29 +00:00
|
|
|
VIR_DIR_CLOSE(dir);
|
2014-06-10 11:47:12 +00:00
|
|
|
VIR_FREE(unique_path);
|
|
|
|
VIR_FREE(host_link);
|
|
|
|
VIR_FREE(host_path);
|
2015-04-27 06:41:41 +00:00
|
|
|
VIR_FREE(buf);
|
2014-06-10 11:47:12 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-10-06 18:18:11 +00:00
|
|
|
/* virGetSCSIHostNumber:
|
|
|
|
* @adapter_name: Name of the host adapter
|
|
|
|
* @result: Return the entry value as unsigned int
|
|
|
|
*
|
|
|
|
* Convert the various forms of scsi_host names into the numeric
|
|
|
|
* host# value that can be used in order to scan sysfs looking for
|
|
|
|
* the specific host.
|
|
|
|
*
|
|
|
|
* Names can be either "scsi_host#" or just "host#", where
|
|
|
|
* "host#" is the back-compat format, but both equate to
|
|
|
|
* the same source adapter. First check if both pool and def
|
|
|
|
* are using same format (easier) - if so, then compare
|
|
|
|
*
|
|
|
|
* Returns 0 on success, and @result has the host number.
|
|
|
|
* Otherwise returns -1.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virGetSCSIHostNumber(const char *adapter_name,
|
|
|
|
unsigned int *result)
|
|
|
|
{
|
|
|
|
/* Specifying adapter like 'host5' is still supported for
|
|
|
|
* back-compat reason.
|
|
|
|
*/
|
|
|
|
if (STRPREFIX(adapter_name, "scsi_host")) {
|
|
|
|
adapter_name += strlen("scsi_host");
|
|
|
|
} else if (STRPREFIX(adapter_name, "fc_host")) {
|
|
|
|
adapter_name += strlen("fc_host");
|
|
|
|
} else if (STRPREFIX(adapter_name, "host")) {
|
|
|
|
adapter_name += strlen("host");
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid adapter name '%s' for SCSI pool"),
|
|
|
|
adapter_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(adapter_name, NULL, 10, result) == -1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid adapter name '%s' for SCSI pool"),
|
|
|
|
adapter_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-06 20:18:23 +00:00
|
|
|
/* virGetSCSIHostNameByParentaddr:
|
|
|
|
* @domain: The domain from the scsi_host parentaddr
|
|
|
|
* @bus: The bus from the scsi_host parentaddr
|
|
|
|
* @slot: The slot from the scsi_host parentaddr
|
|
|
|
* @function: The function from the scsi_host parentaddr
|
|
|
|
* @unique_id: The unique id value for parentaddr
|
|
|
|
*
|
|
|
|
* Generate a parentaddr and find the scsi_host host# for
|
|
|
|
* the provided parentaddr PCI address fields.
|
|
|
|
*
|
|
|
|
* Returns the "host#" string which must be free'd by
|
|
|
|
* the caller or NULL on error
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
virGetSCSIHostNameByParentaddr(unsigned int domain,
|
|
|
|
unsigned int bus,
|
|
|
|
unsigned int slot,
|
|
|
|
unsigned int function,
|
|
|
|
unsigned int unique_id)
|
|
|
|
{
|
|
|
|
char *name = NULL;
|
|
|
|
char *parentaddr = NULL;
|
|
|
|
|
|
|
|
if (virAsprintf(&parentaddr, "%04x:%02x:%02x.%01x",
|
|
|
|
domain, bus, slot, function) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (!(name = virFindSCSIHostByPCI(NULL, parentaddr, unique_id))) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("Failed to find scsi_host using PCI '%s' "
|
|
|
|
"and unique_id='%u'"),
|
|
|
|
parentaddr, unique_id);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(parentaddr);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2013-01-07 17:05:29 +00:00
|
|
|
/* virReadFCHost:
|
|
|
|
* @sysfs_prefix: "fc_host" sysfs path, defaults to SYSFS_FC_HOST_PATH
|
|
|
|
* @host: Host number, E.g. 5 of "fc_host/host5"
|
|
|
|
* @entry: Name of the sysfs entry to read
|
|
|
|
*
|
|
|
|
* Read the value of sysfs "fc_host" entry.
|
|
|
|
*
|
2016-10-11 15:21:19 +00:00
|
|
|
* Returns result as a stringon success, caller must free @result after
|
|
|
|
* Otherwise returns NULL.
|
2013-01-07 17:05:29 +00:00
|
|
|
*/
|
2016-10-11 15:21:19 +00:00
|
|
|
char *
|
2013-01-07 17:05:29 +00:00
|
|
|
virReadFCHost(const char *sysfs_prefix,
|
|
|
|
int host,
|
2016-10-11 15:21:19 +00:00
|
|
|
const char *entry)
|
2013-01-07 17:05:29 +00:00
|
|
|
{
|
|
|
|
char *sysfs_path = NULL;
|
|
|
|
char *p = NULL;
|
|
|
|
char *buf = NULL;
|
2016-10-11 15:21:19 +00:00
|
|
|
char *result = NULL;
|
2013-01-07 17:05:29 +00:00
|
|
|
|
|
|
|
if (virAsprintf(&sysfs_path, "%s/host%d/%s",
|
|
|
|
sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH,
|
2013-07-04 10:17:18 +00:00
|
|
|
host, entry) < 0)
|
2013-01-07 17:05:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileReadAll(sysfs_path, 1024, &buf) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((p = strchr(buf, '\n')))
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
if ((p = strstr(buf, "0x")))
|
|
|
|
p += strlen("0x");
|
|
|
|
else
|
|
|
|
p = buf;
|
|
|
|
|
2016-10-11 15:21:19 +00:00
|
|
|
ignore_value(VIR_STRDUP(result, p));
|
2013-01-07 17:05:29 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-01-07 17:05:29 +00:00
|
|
|
VIR_FREE(sysfs_path);
|
|
|
|
VIR_FREE(buf);
|
2016-10-11 15:21:19 +00:00
|
|
|
return result;
|
2013-01-07 17:05:29 +00:00
|
|
|
}
|
2013-01-07 17:05:31 +00:00
|
|
|
|
2013-05-06 12:45:14 +00:00
|
|
|
bool
|
2013-01-07 17:05:31 +00:00
|
|
|
virIsCapableFCHost(const char *sysfs_prefix,
|
|
|
|
int host)
|
|
|
|
{
|
|
|
|
char *sysfs_path = NULL;
|
2013-05-06 12:45:14 +00:00
|
|
|
bool ret = false;
|
2013-01-07 17:05:31 +00:00
|
|
|
|
2013-05-06 12:45:13 +00:00
|
|
|
if (virAsprintf(&sysfs_path, "%s/host%d",
|
2013-01-07 17:05:31 +00:00
|
|
|
sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH,
|
2013-07-04 10:17:18 +00:00
|
|
|
host) < 0)
|
2013-05-06 12:45:14 +00:00
|
|
|
return false;
|
2013-01-07 17:05:31 +00:00
|
|
|
|
2013-09-13 13:32:43 +00:00
|
|
|
if (virFileExists(sysfs_path))
|
2013-05-06 12:45:14 +00:00
|
|
|
ret = true;
|
2013-01-07 17:05:31 +00:00
|
|
|
|
|
|
|
VIR_FREE(sysfs_path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-05-06 12:45:14 +00:00
|
|
|
bool
|
2013-01-07 17:05:31 +00:00
|
|
|
virIsCapableVport(const char *sysfs_prefix,
|
|
|
|
int host)
|
|
|
|
{
|
|
|
|
char *scsi_host_path = NULL;
|
|
|
|
char *fc_host_path = NULL;
|
2014-11-17 23:39:48 +00:00
|
|
|
bool ret = false;
|
2013-01-07 17:05:31 +00:00
|
|
|
|
|
|
|
if (virAsprintf(&fc_host_path,
|
2013-05-06 12:45:13 +00:00
|
|
|
"%s/host%d/%s",
|
2013-01-07 17:05:31 +00:00
|
|
|
sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH,
|
|
|
|
host,
|
2013-07-04 10:17:18 +00:00
|
|
|
"vport_create") < 0)
|
2013-05-06 12:45:14 +00:00
|
|
|
return false;
|
2013-01-07 17:05:31 +00:00
|
|
|
|
|
|
|
if (virAsprintf(&scsi_host_path,
|
2013-05-06 12:45:13 +00:00
|
|
|
"%s/host%d/%s",
|
2013-01-07 17:05:31 +00:00
|
|
|
sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_HOST_PATH,
|
|
|
|
host,
|
2013-07-04 10:17:18 +00:00
|
|
|
"vport_create") < 0)
|
2013-01-07 17:05:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-09-13 13:32:43 +00:00
|
|
|
if (virFileExists(fc_host_path) ||
|
|
|
|
virFileExists(scsi_host_path))
|
2013-05-06 12:45:14 +00:00
|
|
|
ret = true;
|
2013-01-07 17:05:31 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-01-07 17:05:31 +00:00
|
|
|
VIR_FREE(fc_host_path);
|
|
|
|
VIR_FREE(scsi_host_path);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-01-07 17:05:34 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
virManageVport(const int parent_host,
|
|
|
|
const char *wwpn,
|
|
|
|
const char *wwnn,
|
|
|
|
int operation)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
char *operation_path = NULL, *vport_name = NULL;
|
|
|
|
const char *operation_file = NULL;
|
|
|
|
|
|
|
|
switch (operation) {
|
|
|
|
case VPORT_CREATE:
|
|
|
|
operation_file = "vport_create";
|
|
|
|
break;
|
|
|
|
case VPORT_DELETE:
|
|
|
|
operation_file = "vport_delete";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Invalid vport operation (%d)"), operation);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&operation_path,
|
2013-05-06 12:45:13 +00:00
|
|
|
"%s/host%d/%s",
|
2013-01-07 17:05:34 +00:00
|
|
|
SYSFS_FC_HOST_PATH,
|
|
|
|
parent_host,
|
2013-07-04 10:17:18 +00:00
|
|
|
operation_file) < 0)
|
2013-01-07 17:05:34 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!virFileExists(operation_path)) {
|
|
|
|
VIR_FREE(operation_path);
|
|
|
|
if (virAsprintf(&operation_path,
|
2013-05-06 12:45:13 +00:00
|
|
|
"%s/host%d/%s",
|
2013-01-07 17:05:34 +00:00
|
|
|
SYSFS_SCSI_HOST_PATH,
|
|
|
|
parent_host,
|
2013-07-04 10:17:18 +00:00
|
|
|
operation_file) < 0)
|
2013-01-07 17:05:34 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!virFileExists(operation_path)) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("vport operation '%s' is not supported for host%d"),
|
|
|
|
operation_file, parent_host);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&vport_name,
|
|
|
|
"%s:%s",
|
2013-06-28 09:59:51 +00:00
|
|
|
wwpn,
|
2013-07-04 10:17:18 +00:00
|
|
|
wwnn) < 0)
|
2013-01-07 17:05:34 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileWriteStr(operation_path, vport_name, 0) == 0)
|
|
|
|
ret = 0;
|
|
|
|
else
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Write of '%s' to '%s' during "
|
|
|
|
"vport create/delete failed"),
|
|
|
|
vport_name, operation_path);
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-01-07 17:05:34 +00:00
|
|
|
VIR_FREE(vport_name);
|
|
|
|
VIR_FREE(operation_path);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-03-25 16:43:40 +00:00
|
|
|
|
2016-10-11 15:21:19 +00:00
|
|
|
/* virGetFCHostNameByWWN:
|
2013-03-25 16:43:40 +00:00
|
|
|
*
|
2013-05-06 12:45:15 +00:00
|
|
|
* Iterate over the sysfs tree to get FC host name (e.g. host5)
|
2014-01-23 10:18:36 +00:00
|
|
|
* by the provided "wwnn,wwpn" pair.
|
|
|
|
*
|
|
|
|
* Returns the FC host name which must be freed by the caller,
|
|
|
|
* or NULL on failure.
|
2013-03-25 16:43:40 +00:00
|
|
|
*/
|
|
|
|
char *
|
|
|
|
virGetFCHostNameByWWN(const char *sysfs_prefix,
|
|
|
|
const char *wwnn,
|
|
|
|
const char *wwpn)
|
|
|
|
{
|
|
|
|
const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH;
|
|
|
|
struct dirent *entry = NULL;
|
|
|
|
DIR *dir = NULL;
|
|
|
|
char *wwnn_path = NULL;
|
|
|
|
char *wwpn_path = NULL;
|
|
|
|
char *wwnn_buf = NULL;
|
|
|
|
char *wwpn_buf = NULL;
|
|
|
|
char *p;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
2016-06-21 14:34:08 +00:00
|
|
|
if (virDirOpen(&dir, prefix) < 0)
|
2013-03-25 16:43:40 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
# define READ_WWN(wwn_path, buf) \
|
|
|
|
do { \
|
|
|
|
if (virFileReadAll(wwn_path, 1024, &buf) < 0) \
|
|
|
|
goto cleanup; \
|
|
|
|
if ((p = strchr(buf, '\n'))) \
|
|
|
|
*p = '\0'; \
|
|
|
|
if (STRPREFIX(buf, "0x")) \
|
|
|
|
p = buf + strlen("0x"); \
|
|
|
|
else \
|
|
|
|
p = buf; \
|
|
|
|
} while (0)
|
|
|
|
|
2014-04-25 20:45:49 +00:00
|
|
|
while (virDirRead(dir, &entry, prefix) > 0) {
|
2013-05-06 12:45:13 +00:00
|
|
|
if (virAsprintf(&wwnn_path, "%s/%s/node_name", prefix,
|
2013-07-04 10:17:18 +00:00
|
|
|
entry->d_name) < 0)
|
2013-03-25 16:43:40 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!virFileExists(wwnn_path)) {
|
|
|
|
VIR_FREE(wwnn_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
READ_WWN(wwnn_path, wwnn_buf);
|
|
|
|
|
|
|
|
if (STRNEQ(wwnn, p)) {
|
|
|
|
VIR_FREE(wwnn_buf);
|
|
|
|
VIR_FREE(wwnn_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-05-06 12:45:13 +00:00
|
|
|
if (virAsprintf(&wwpn_path, "%s/%s/port_name", prefix,
|
2013-07-04 10:17:18 +00:00
|
|
|
entry->d_name) < 0)
|
2013-03-25 16:43:40 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!virFileExists(wwpn_path)) {
|
|
|
|
VIR_FREE(wwnn_buf);
|
|
|
|
VIR_FREE(wwnn_path);
|
|
|
|
VIR_FREE(wwpn_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
READ_WWN(wwpn_path, wwpn_buf);
|
|
|
|
|
|
|
|
if (STRNEQ(wwpn, p)) {
|
|
|
|
VIR_FREE(wwnn_path);
|
|
|
|
VIR_FREE(wwpn_path);
|
|
|
|
VIR_FREE(wwnn_buf);
|
|
|
|
VIR_FREE(wwpn_buf);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-05-24 07:19:51 +00:00
|
|
|
ignore_value(VIR_STRDUP(ret, entry->d_name));
|
2013-03-25 16:43:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-03-25 16:43:40 +00:00
|
|
|
# undef READ_WWN
|
2016-06-21 10:40:29 +00:00
|
|
|
VIR_DIR_CLOSE(dir);
|
2013-03-25 16:43:40 +00:00
|
|
|
VIR_FREE(wwnn_path);
|
|
|
|
VIR_FREE(wwpn_path);
|
|
|
|
VIR_FREE(wwnn_buf);
|
|
|
|
VIR_FREE(wwpn_buf);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-03-25 16:43:42 +00:00
|
|
|
|
|
|
|
# define PORT_STATE_ONLINE "Online"
|
|
|
|
|
|
|
|
/* virFindFCHostCapableVport:
|
|
|
|
*
|
|
|
|
* Iterate over the sysfs and find out the first online HBA which
|
2013-05-06 12:45:16 +00:00
|
|
|
* supports vport, and not saturated. Returns the host name (e.g.
|
|
|
|
* host5) on success, or NULL on failure.
|
2013-03-25 16:43:42 +00:00
|
|
|
*/
|
|
|
|
char *
|
|
|
|
virFindFCHostCapableVport(const char *sysfs_prefix)
|
|
|
|
{
|
|
|
|
const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_FC_HOST_PATH;
|
|
|
|
DIR *dir = NULL;
|
|
|
|
struct dirent *entry = NULL;
|
|
|
|
char *max_vports = NULL;
|
|
|
|
char *vports = NULL;
|
|
|
|
char *state = NULL;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
2016-06-21 14:34:08 +00:00
|
|
|
if (virDirOpen(&dir, prefix) < 0)
|
2013-03-25 16:43:42 +00:00
|
|
|
return NULL;
|
|
|
|
|
2014-04-25 20:45:49 +00:00
|
|
|
while (virDirRead(dir, &entry, prefix) > 0) {
|
2013-03-25 16:43:42 +00:00
|
|
|
unsigned int host;
|
|
|
|
char *p = NULL;
|
|
|
|
|
|
|
|
p = entry->d_name + strlen("host");
|
|
|
|
if (virStrToLong_ui(p, NULL, 10, &host) == -1) {
|
|
|
|
VIR_DEBUG("Failed to parse host number from '%s'",
|
|
|
|
entry->d_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-05-06 12:45:16 +00:00
|
|
|
if (!virIsCapableVport(prefix, host))
|
2013-03-25 16:43:42 +00:00
|
|
|
continue;
|
|
|
|
|
2016-10-11 15:21:19 +00:00
|
|
|
if (!(state = virReadFCHost(prefix, host, "port_state"))) {
|
2013-03-25 16:43:42 +00:00
|
|
|
VIR_DEBUG("Failed to read port_state for host%d", host);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the not online FC host */
|
|
|
|
if (STRNEQ(state, PORT_STATE_ONLINE)) {
|
|
|
|
VIR_FREE(state);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VIR_FREE(state);
|
|
|
|
|
2016-10-11 15:21:19 +00:00
|
|
|
if (!(max_vports = virReadFCHost(prefix, host, "max_npiv_vports"))) {
|
2013-03-25 16:43:42 +00:00
|
|
|
VIR_DEBUG("Failed to read max_npiv_vports for host%d", host);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-10-11 15:21:19 +00:00
|
|
|
if (!(vports = virReadFCHost(prefix, host, "npiv_vports_inuse"))) {
|
2013-03-25 16:43:42 +00:00
|
|
|
VIR_DEBUG("Failed to read npiv_vports_inuse for host%d", host);
|
|
|
|
VIR_FREE(max_vports);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare from the strings directly, instead of converting
|
|
|
|
* the strings to integers first
|
|
|
|
*/
|
|
|
|
if ((strlen(max_vports) >= strlen(vports)) ||
|
|
|
|
((strlen(max_vports) == strlen(vports)) &&
|
|
|
|
strcmp(max_vports, vports) > 0)) {
|
2013-05-24 07:19:51 +00:00
|
|
|
ignore_value(VIR_STRDUP(ret, entry->d_name));
|
2013-03-25 16:43:42 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(max_vports);
|
|
|
|
VIR_FREE(vports);
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2016-06-21 10:40:29 +00:00
|
|
|
VIR_DIR_CLOSE(dir);
|
2013-03-25 16:43:42 +00:00
|
|
|
VIR_FREE(max_vports);
|
|
|
|
VIR_FREE(vports);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-01-07 17:05:29 +00:00
|
|
|
#else
|
2014-06-10 15:09:20 +00:00
|
|
|
int
|
|
|
|
virReadSCSIUniqueId(const char *sysfs_prefix ATTRIBUTE_UNUSED,
|
|
|
|
int host ATTRIBUTE_UNUSED,
|
2014-07-21 17:26:00 +00:00
|
|
|
int *result ATTRIBUTE_UNUSED)
|
2014-06-10 15:09:20 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-06-10 11:47:12 +00:00
|
|
|
char *
|
|
|
|
virFindSCSIHostByPCI(const char *sysfs_prefix ATTRIBUTE_UNUSED,
|
|
|
|
const char *parentaddr ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int unique_id ATTRIBUTE_UNUSED)
|
2014-10-06 18:18:11 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virGetSCSIHostNumber(const char *adapter_name ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *result ATTRIBUTE_UNUSED)
|
2014-10-06 20:18:23 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
2014-10-29 18:20:48 +00:00
|
|
|
return -1;
|
2014-10-06 20:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
virGetSCSIHostNameByParentaddr(unsigned int domain ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int bus ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int slot ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int function ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int unique_id ATTRIBUTE_UNUSED)
|
2014-06-10 11:47:12 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
2014-07-21 17:26:00 +00:00
|
|
|
return NULL;
|
2014-06-10 11:47:12 +00:00
|
|
|
}
|
|
|
|
|
2016-10-11 15:21:19 +00:00
|
|
|
char *
|
2013-01-07 17:05:29 +00:00
|
|
|
virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED,
|
|
|
|
int host ATTRIBUTE_UNUSED,
|
2016-10-11 15:21:19 +00:00
|
|
|
const char *entry ATTRIBUTE_UNUSED)
|
2013-01-07 17:05:29 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
2016-10-11 15:21:19 +00:00
|
|
|
return NULL;
|
2013-01-07 17:05:29 +00:00
|
|
|
}
|
2013-01-07 17:05:31 +00:00
|
|
|
|
2013-05-06 12:45:14 +00:00
|
|
|
bool
|
2013-03-28 12:17:05 +00:00
|
|
|
virIsCapableFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED,
|
|
|
|
int host ATTRIBUTE_UNUSED)
|
2013-01-07 17:05:31 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
2013-05-06 12:45:14 +00:00
|
|
|
return false;
|
2013-01-07 17:05:31 +00:00
|
|
|
}
|
|
|
|
|
2013-05-06 12:45:14 +00:00
|
|
|
bool
|
2013-03-28 12:20:41 +00:00
|
|
|
virIsCapableVport(const char *sysfs_prefix ATTRIBUTE_UNUSED,
|
|
|
|
int host ATTRIBUTE_UNUSED)
|
2013-01-07 17:05:31 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
2013-05-06 12:45:14 +00:00
|
|
|
return false;
|
2013-01-07 17:05:31 +00:00
|
|
|
}
|
2013-01-07 17:05:34 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
virManageVport(const int parent_host ATTRIBUTE_UNUSED,
|
|
|
|
const char *wwpn ATTRIBUTE_UNUSED,
|
|
|
|
const char *wwnn ATTRIBUTE_UNUSED,
|
|
|
|
int operation ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:40 +00:00
|
|
|
char *
|
|
|
|
virGetFCHostNameByWWN(const char *sysfs_prefix ATTRIBUTE_UNUSED,
|
|
|
|
const char *wwnn ATTRIBUTE_UNUSED,
|
|
|
|
const char *wwpn ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:42 +00:00
|
|
|
char *
|
|
|
|
virFindFCHostCapableVport(const char *sysfs_prefix ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-07 17:05:29 +00:00
|
|
|
#endif /* __linux__ */
|
2013-04-17 15:50:56 +00:00
|
|
|
|
2013-05-24 15:35:01 +00:00
|
|
|
/**
|
|
|
|
* virParseOwnershipIds:
|
|
|
|
*
|
|
|
|
* Parse the usual "uid:gid" ownership specification into uid_t and
|
|
|
|
* gid_t passed as parameters. NULL value for those parameters mean
|
|
|
|
* the information is not needed. Also, none of those values are
|
|
|
|
* changed in case of any error.
|
|
|
|
*
|
|
|
|
* Returns -1 on error, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virParseOwnershipIds(const char *label, uid_t *uidPtr, gid_t *gidPtr)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
uid_t theuid;
|
|
|
|
gid_t thegid;
|
|
|
|
char *tmp_label = NULL;
|
|
|
|
char *sep = NULL;
|
|
|
|
char *owner = NULL;
|
|
|
|
char *group = NULL;
|
|
|
|
|
|
|
|
if (VIR_STRDUP(tmp_label, label) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Split label */
|
|
|
|
sep = strchr(tmp_label, ':');
|
|
|
|
if (sep == NULL) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Failed to parse uid and gid from '%s'"),
|
|
|
|
label);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*sep = '\0';
|
|
|
|
owner = tmp_label;
|
|
|
|
group = sep + 1;
|
|
|
|
|
|
|
|
/* Parse owner and group, error message is defined by
|
|
|
|
* virGetUserID or virGetGroupID.
|
|
|
|
*/
|
|
|
|
if (virGetUserID(owner, &theuid) < 0 ||
|
|
|
|
virGetGroupID(group, &thegid) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (uidPtr)
|
|
|
|
*uidPtr = theuid;
|
|
|
|
if (gidPtr)
|
|
|
|
*gidPtr = thegid;
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2013-05-24 15:35:01 +00:00
|
|
|
VIR_FREE(tmp_label);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2013-10-09 09:52:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virGetEnvBlockSUID:
|
|
|
|
* @name: the environment variable name
|
|
|
|
*
|
|
|
|
* Obtain an environment variable which is unsafe to
|
|
|
|
* use when running setuid. If running setuid, a NULL
|
|
|
|
* value will be returned
|
|
|
|
*/
|
|
|
|
const char *virGetEnvBlockSUID(const char *name)
|
|
|
|
{
|
2016-04-11 14:21:59 +00:00
|
|
|
return secure_getenv(name); /* exempt from syntax-check */
|
2013-10-09 09:52:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virGetEnvBlockSUID:
|
|
|
|
* @name: the environment variable name
|
|
|
|
*
|
|
|
|
* Obtain an environment variable which is safe to
|
|
|
|
* use when running setuid. The value will be returned
|
|
|
|
* even when running setuid
|
|
|
|
*/
|
|
|
|
const char *virGetEnvAllowSUID(const char *name)
|
|
|
|
{
|
2016-04-11 14:21:59 +00:00
|
|
|
return getenv(name); /* exempt from syntax-check */
|
2013-10-09 09:52:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virIsSUID:
|
|
|
|
* Return a true value if running setuid. Does not
|
|
|
|
* check for elevated capabilities bits.
|
|
|
|
*/
|
|
|
|
bool virIsSUID(void)
|
|
|
|
{
|
|
|
|
return getuid() != geteuid();
|
|
|
|
}
|
2014-03-05 17:20:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
static time_t selfLastChanged;
|
|
|
|
|
|
|
|
time_t virGetSelfLastChanged(void)
|
|
|
|
{
|
|
|
|
return selfLastChanged;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void virUpdateSelfLastChanged(const char *path)
|
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sb.st_ctime > selfLastChanged) {
|
|
|
|
VIR_DEBUG("Setting self last changed to %lld for '%s'",
|
|
|
|
(long long)sb.st_ctime, path);
|
|
|
|
selfLastChanged = sb.st_ctime;
|
|
|
|
}
|
|
|
|
}
|
2014-07-15 12:34:13 +00:00
|
|
|
|
2014-08-22 08:28:17 +00:00
|
|
|
#ifndef WIN32
|
|
|
|
|
2014-07-15 12:34:13 +00:00
|
|
|
/**
|
|
|
|
* virGetListenFDs:
|
|
|
|
*
|
|
|
|
* Parse LISTEN_PID and LISTEN_FDS passed from caller.
|
|
|
|
*
|
|
|
|
* Returns number of passed FDs.
|
|
|
|
*/
|
|
|
|
unsigned int
|
|
|
|
virGetListenFDs(void)
|
|
|
|
{
|
|
|
|
const char *pidstr;
|
|
|
|
const char *fdstr;
|
|
|
|
size_t i = 0;
|
|
|
|
unsigned long long procid;
|
|
|
|
unsigned int nfds;
|
|
|
|
|
|
|
|
VIR_DEBUG("Setting up networking from caller");
|
|
|
|
|
|
|
|
if (!(pidstr = virGetEnvAllowSUID("LISTEN_PID"))) {
|
|
|
|
VIR_DEBUG("No LISTEN_PID from caller");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ull(pidstr, NULL, 10, &procid) < 0) {
|
|
|
|
VIR_DEBUG("Malformed LISTEN_PID from caller %s", pidstr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pid_t)procid != getpid()) {
|
2016-10-06 14:54:41 +00:00
|
|
|
VIR_DEBUG("LISTEN_PID %s is not for us %lld",
|
|
|
|
pidstr, (long long) getpid());
|
2014-07-15 12:34:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(fdstr = virGetEnvAllowSUID("LISTEN_FDS"))) {
|
|
|
|
VIR_DEBUG("No LISTEN_FDS from caller");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(fdstr, NULL, 10, &nfds) < 0) {
|
|
|
|
VIR_DEBUG("Malformed LISTEN_FDS from caller %s", fdstr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsetenv("LISTEN_PID");
|
|
|
|
unsetenv("LISTEN_FDS");
|
|
|
|
|
|
|
|
VIR_DEBUG("Got %u file descriptors", nfds);
|
|
|
|
|
|
|
|
for (i = 0; i < nfds; i++) {
|
|
|
|
int fd = STDERR_FILENO + i + 1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Disabling inheritance of passed FD %d", fd);
|
|
|
|
|
2014-11-13 14:28:18 +00:00
|
|
|
if (virSetInherit(fd, false) < 0)
|
2014-07-15 12:34:13 +00:00
|
|
|
VIR_WARN("Couldn't disable inheritance of passed FD %d", fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nfds;
|
|
|
|
}
|
2014-08-22 08:28:17 +00:00
|
|
|
|
|
|
|
#else /* WIN32 */
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
virGetListenFDs(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* WIN32 */
|
2015-02-02 10:26:49 +00:00
|
|
|
|
2015-02-03 13:20:25 +00:00
|
|
|
#ifndef WIN32
|
2015-02-02 10:26:49 +00:00
|
|
|
long virGetSystemPageSize(void)
|
|
|
|
{
|
|
|
|
return sysconf(_SC_PAGESIZE);
|
|
|
|
}
|
2015-02-03 13:20:25 +00:00
|
|
|
#else /* WIN32 */
|
|
|
|
long virGetSystemPageSize(void)
|
|
|
|
{
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif /* WIN32 */
|
2015-02-02 10:26:49 +00:00
|
|
|
|
|
|
|
long virGetSystemPageSizeKB(void)
|
|
|
|
{
|
|
|
|
long val = virGetSystemPageSize();
|
|
|
|
if (val < 0)
|
|
|
|
return val;
|
|
|
|
return val / 1024;
|
|
|
|
}
|
2015-03-04 14:08:09 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virMemoryLimitTruncate
|
|
|
|
*
|
|
|
|
* Return truncated memory limit to VIR_DOMAIN_MEMORY_PARAM_UNLIMITED as maximum
|
|
|
|
* which means that the limit is not set => unlimited.
|
|
|
|
*/
|
|
|
|
unsigned long long
|
|
|
|
virMemoryLimitTruncate(unsigned long long value)
|
|
|
|
{
|
|
|
|
return value < VIR_DOMAIN_MEMORY_PARAM_UNLIMITED ? value :
|
|
|
|
VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virMemoryLimitIsSet
|
|
|
|
*
|
|
|
|
* Returns true if the limit is set and false for unlimited value.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virMemoryLimitIsSet(unsigned long long value)
|
|
|
|
{
|
|
|
|
return value < VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
|
|
|
|
}
|
2015-05-05 15:58:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virMemoryMaxValue
|
|
|
|
*
|
2015-05-21 14:50:19 +00:00
|
|
|
* @capped: whether the value must fit into unsigned long
|
2015-05-05 15:58:49 +00:00
|
|
|
* (long long is assumed otherwise)
|
|
|
|
*
|
2015-12-10 13:36:51 +00:00
|
|
|
* Note: This function is mocked in tests/qemuxml2argvmock.c for test stability
|
|
|
|
*
|
2015-05-05 15:58:49 +00:00
|
|
|
* Returns the maximum possible memory value in bytes.
|
|
|
|
*/
|
|
|
|
unsigned long long
|
2015-05-21 14:50:19 +00:00
|
|
|
virMemoryMaxValue(bool capped)
|
2015-05-05 15:58:49 +00:00
|
|
|
{
|
|
|
|
/* On 32-bit machines, our bound is 0xffffffff * KiB. On 64-bit
|
|
|
|
* machines, our bound is off_t (2^63). */
|
2015-05-21 14:50:19 +00:00
|
|
|
if (capped && sizeof(unsigned long) < sizeof(long long))
|
2015-05-05 15:58:49 +00:00
|
|
|
return 1024ull * ULONG_MAX;
|
|
|
|
else
|
|
|
|
return LLONG_MAX;
|
|
|
|
}
|