libvirt/src/util/virutil.h

228 lines
7.2 KiB
C
Raw Normal View History

/*
2012-12-13 17:44:57 +00:00
* virutil.h: common, generic utility functions
*
* Copyright (C) 2010-2014 Red Hat, Inc.
* 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
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "internal.h"
#include <unistd.h>
#include <sys/types.h>
int virSetBlocking(int fd, bool blocking) G_GNUC_WARN_UNUSED_RESULT;
int virSetNonBlock(int fd) G_GNUC_WARN_UNUSED_RESULT;
int virSetInherit(int fd, bool inherit) G_GNUC_WARN_UNUSED_RESULT;
int virSetCloseExec(int fd) G_GNUC_WARN_UNUSED_RESULT;
int virSetSockReuseAddr(int fd, bool fatal) G_GNUC_WARN_UNUSED_RESULT;
2009-01-20 16:36:34 +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
int virSetUIDGID(uid_t uid, gid_t gid, gid_t *groups, int ngroups);
int virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
unsigned long long capBits,
bool clearExistingCaps);
void virWaitForDevices(void);
int virScaleInteger(unsigned long long *value, const char *suffix,
unsigned long long scale, unsigned long long limit)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
int virParseVersionString(const char *str, unsigned long *version,
bool allowMissing);
char *virFormatIntDecimal(char *buf, size_t buflen, int val)
ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT;
unsigned long long
virFormatIntPretty(unsigned long long val,
const char **unit);
int virDiskNameParse(const char *name, int *disk, int *partition);
int virDiskNameToIndex(const char* str);
char *virIndexToDiskName(int idx, const char *prefix);
/* No-op workarounds for functionality missing in mingw. */
#ifndef HAVE_GETUID
static inline int getuid(void)
{ return 0; }
#endif
2008-09-05 12:03:45 +00:00
#ifndef HAVE_GETEUID
static inline int geteuid(void)
{ return 0; }
#endif
#ifndef HAVE_GETGID
static inline int getgid(void)
{ return 0; }
#endif
2008-09-05 12:03:45 +00:00
#ifndef HAVE_GETEGID
static inline int getegid(void)
{ return 0; }
#endif
#ifdef FUNC_PTHREAD_SIGMASK_BROKEN
# undef pthread_sigmask
static inline int pthread_sigmask(int how,
const void *set,
void *old)
{
(void) how;
(void) set;
(void) old;
return 0;
}
#endif
char *virGetHostname(void);
char *virGetHostnameQuiet(void);
char *virGetUserDirectory(void);
char *virGetUserDirectoryByUID(uid_t uid);
char *virGetUserConfigDirectory(void);
char *virGetUserCacheDirectory(void);
char *virGetUserRuntimeDirectory(void) G_GNUC_NO_INLINE;
char *virGetUserShell(uid_t uid);
char *virGetUserName(uid_t uid) G_GNUC_NO_INLINE;
char *virGetGroupName(gid_t gid) G_GNUC_NO_INLINE;
int virGetGroupList(uid_t uid, gid_t group, gid_t **groups)
ATTRIBUTE_NONNULL(3);
int virGetUserID(const char *name,
uid_t *uid) G_GNUC_WARN_UNUSED_RESULT;
int virGetGroupID(const char *name,
gid_t *gid) G_GNUC_WARN_UNUSED_RESULT;
2009-01-22 19:41:48 +00:00
bool virDoesUserExist(const char *name);
bool virDoesGroupExist(const char *name);
bool virValidateWWN(const char *wwn);
int virGetDeviceID(const char *path,
int *maj,
int *min) G_GNUC_NO_INLINE;
int virSetDeviceUnprivSGIO(const char *path,
const char *sysfs_dir,
int unpriv_sgio);
int virGetDeviceUnprivSGIO(const char *path,
const char *sysfs_dir,
int *unpriv_sgio);
char *virGetUnprivSGIOSysfsPath(const char *path,
const char *sysfs_dir);
int virParseOwnershipIds(const char *label, uid_t *uidPtr, gid_t *gidPtr);
time_t virGetSelfLastChanged(void);
void virUpdateSelfLastChanged(const char *path);
long virGetSystemPageSize(void) G_GNUC_NO_INLINE;
long virGetSystemPageSizeKB(void) G_GNUC_NO_INLINE;
unsigned long long virMemoryLimitTruncate(unsigned long long value);
bool virMemoryLimitIsSet(unsigned long long value);
unsigned long long virMemoryMaxValue(bool ulong) G_GNUC_NO_INLINE;
bool virHostHasIOMMU(void);
char *virHostGetDRMRenderNode(void) G_GNUC_NO_INLINE;
/* Kernel cmdline match and comparison strategy for arg=value pairs */
typedef enum {
/* substring comparison of argument values */
VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX = 1,
/* strict string comparison of argument values */
VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ = 2,
/* look for any occurrence of the argument with the expected value,
* this should be used when an argument set to the expected value overrides
* all the other occurrences of the argument, e.g. when looking for 'arg=1'
* in 'arg=0 arg=1 arg=0' the search would succeed with this flag
*/
VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST = 4,
/* look for the last occurrence of argument with the expected value,
* this should be used when the last occurrence of the argument overrides
* all the other ones, e.g. when looking for 'arg=1' in 'arg=0 arg=1' the
* search would succeed with this flag, but in 'arg=1 arg=0' it would not,
* because 'arg=0' overrides all the previous occurrences of 'arg'
*/
VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST = 8,
} virKernelCmdlineFlags;
const char *virKernelCmdlineNextParam(const char *cmdline,
char **param,
char **val);
bool virKernelCmdlineMatchParam(const char *cmdline,
const char *arg,
const char **values,
size_t len_values,
virKernelCmdlineFlags flags);
/**
* VIR_ASSIGN_IS_OVERFLOW:
* @rvalue: value that is checked (evaluated twice)
* @lvalue: value that the check is against (used in typeof())
*
* This macro assigns @lvalue to @rvalue and evaluates as true if the value of
* @rvalue did not fit into the @lvalue.
*/
#define VIR_ASSIGN_IS_OVERFLOW(lvalue, rvalue) \
(((lvalue) = (rvalue)) != (rvalue))
char *virGetPassword(void);
/*
* virPipe:
*
* Open a pair of FDs which can be used to communicate
* with each other. The FDs will have O_CLOEXEC set.
* This will report a libvirt error on failure.
*
* Returns: -1 on error, 0 on success
*/
int virPipe(int fds[2]);
/*
* virPipeQuiet:
*
* Open a pair of FDs which can be used to communicate
* with each other. The FDs will have O_CLOEXEC set.
* This will set errno on failure.
*
* Returns: -1 on error, 0 on success
*/
int virPipeQuiet(int fds[2]);
/*
* virPipe:
*
* Open a pair of FDs which can be used to communicate
* with each other. The FDs will have O_CLOEXEC and
* O_NONBLOCK set.
* This will report a libvirt error on failure.
*
* Returns: -1 on error, 0 on success
*/
int virPipeNonBlock(int fds[2]);