2004-07-16 19:56:36 +00:00
/***
2006-06-19 21:53:48 +00:00
This file is part of PulseAudio .
2007-01-04 13:43:45 +00:00
2007-02-13 15:35:19 +00:00
Copyright 2004 - 2006 Lennart Poettering
Copyright 2004 Joe Marcus Clarke
2007-02-14 12:13:49 +00:00
Copyright 2006 - 2007 Pierre Ossman < ossman @ cendio . se > for Cendio AB
2007-02-13 15:35:19 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is free software ; you can redistribute it and / or modify
2004-11-14 14:58:54 +00:00
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 .
2007-01-04 13:43:45 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is distributed in the hope that it will be useful , but
2004-07-16 19:56:36 +00:00
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2004-11-14 14:58:54 +00:00
Lesser General Public License for more details .
2007-01-04 13:43:45 +00:00
2004-11-14 14:58:54 +00:00
You should have received a copy of the GNU Lesser General Public
2014-11-26 14:14:51 +01:00
License along with PulseAudio ; if not , see < http : //www.gnu.org/licenses/>.
2004-07-16 19:56:36 +00:00
* * */
2004-07-16 19:16:42 +00:00
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
2015-04-10 12:44:02 +03:00
# include <math.h>
2004-07-10 20:56:38 +00:00
# include <stdarg.h>
# include <stdlib.h>
2004-07-07 00:22:46 +00:00
# include <signal.h>
2004-06-29 16:48:37 +00:00
# include <errno.h>
2004-06-23 23:17:30 +00:00
# include <string.h>
# include <stdio.h>
# include <fcntl.h>
2004-06-29 16:48:37 +00:00
# include <unistd.h>
2006-01-10 17:51:06 +00:00
# include <limits.h>
# include <ctype.h>
2004-06-29 16:48:37 +00:00
# include <sys/types.h>
2004-09-23 15:57:15 +00:00
# include <sys/stat.h>
2008-05-15 23:34:41 +00:00
# include <dirent.h>
2011-01-06 00:51:33 +01:00
# ifdef HAVE_LANGINFO_H
2008-08-09 03:46:23 +02:00
# include <langinfo.h>
2011-01-06 00:51:33 +01:00
# endif
# ifdef HAVE_UNAME
2008-09-05 15:42:39 +03:00
# include <sys/utsname.h>
2011-01-06 00:51:33 +01:00
# endif
2006-01-10 17:51:06 +00:00
2011-01-04 16:48:06 +01:00
# if defined(HAVE_REGEX_H)
# include <regex.h>
# elif defined(HAVE_PCREPOSIX_H)
# include <pcreposix.h>
# endif
2015-05-20 22:35:27 +03:00
# ifdef HAVE_STRTOD_L
2015-11-28 10:01:05 +01:00
# ifdef HAVE_LOCALE_H
2007-10-28 19:13:50 +00:00
# include <locale.h>
2015-11-28 10:01:05 +01:00
# endif
# ifdef HAVE_XLOCALE_H
2015-05-20 22:35:28 +03:00
# include <xlocale.h>
2007-10-28 19:13:50 +00:00
# endif
2015-11-28 10:01:05 +01:00
# endif
2007-10-28 19:13:50 +00:00
2006-01-10 17:51:06 +00:00
# ifdef HAVE_SCHED_H
2004-09-01 00:23:51 +00:00
# include <sched.h>
2009-06-19 04:19:08 +02:00
# if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
# define SCHED_RESET_ON_FORK 0x40000000
# endif
2006-01-10 17:51:06 +00:00
# endif
# ifdef HAVE_SYS_RESOURCE_H
2004-09-01 00:23:51 +00:00
# include <sys/resource.h>
2006-01-10 17:51:06 +00:00
# endif
2007-05-25 20:35:30 +00:00
# ifdef HAVE_SYS_CAPABILITY_H
# include <sys/capability.h>
# endif
2007-10-28 19:13:50 +00:00
# ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
# endif
2006-01-10 17:51:06 +00:00
# ifdef HAVE_PTHREAD
# include <pthread.h>
# endif
# ifdef HAVE_NETDB_H
2004-11-09 00:14:07 +00:00
# include <netdb.h>
2006-01-10 17:51:06 +00:00
# endif
# ifdef HAVE_WINDOWS_H
# include <windows.h>
# endif
2004-06-23 23:17:30 +00:00
2011-01-06 02:10:45 +01:00
# ifndef ENOTSUP
# define ENOTSUP 135
# endif
2006-01-10 17:51:06 +00:00
# ifdef HAVE_PWD_H
# include <pwd.h>
# endif
2006-05-17 20:09:57 +00:00
2006-01-10 17:51:06 +00:00
# ifdef HAVE_GRP_H
# include <grp.h>
# endif
2007-10-28 19:13:50 +00:00
# ifdef HAVE_LIBSAMPLERATE
2006-05-17 20:09:57 +00:00
# include <samplerate.h>
2007-10-28 19:13:50 +00:00
# endif
2006-01-10 17:51:06 +00:00
2009-02-06 00:25:47 +01:00
# ifdef __APPLE__
2011-03-20 17:57:49 +01:00
# include <mach/mach_init.h>
# include <mach/thread_act.h>
# include <mach/thread_policy.h>
# include <sys/sysctl.h>
2009-02-06 00:25:47 +01:00
# endif
2009-06-19 04:19:08 +02:00
# ifdef HAVE_DBUS
# include "rtkit.h"
# endif
2013-09-18 16:22:00 +02:00
# if defined(__linux__) && !defined(__ANDROID__)
2009-08-12 21:36:52 +02:00
# include <sys/personality.h>
# endif
2006-06-19 21:53:48 +00:00
# include <pulse/xmalloc.h>
# include <pulse/util.h>
2007-10-28 19:13:50 +00:00
# include <pulse/utf8.h>
2006-05-17 16:34:18 +00:00
2006-06-19 21:53:48 +00:00
# include <pulsecore/core-error.h>
2011-01-04 17:03:13 +01:00
# include <pulsecore/socket.h>
2006-06-19 21:53:48 +00:00
# include <pulsecore/log.h>
2007-10-28 19:13:50 +00:00
# include <pulsecore/macro.h>
# include <pulsecore/thread.h>
2009-02-04 17:19:15 +01:00
# include <pulsecore/strbuf.h>
2009-08-21 16:02:57 -06:00
# include <pulsecore/usergroup.h>
2009-09-02 00:34:27 +02:00
# include <pulsecore/strlist.h>
2009-11-11 04:50:32 +01:00
# include <pulsecore/cpu-x86.h>
2011-01-06 01:35:27 +01:00
# include <pulsecore/pipe.h>
2016-08-18 01:06:47 +02:00
# include <pulsecore/once.h>
2006-02-17 12:10:58 +00:00
2006-05-17 20:09:57 +00:00
# include "core-util.h"
2004-06-23 23:17:30 +00:00
2006-07-17 11:26:29 +00:00
/* Not all platforms have this */
# ifndef MSG_NOSIGNAL
# define MSG_NOSIGNAL 0
# endif
2010-02-21 21:59:53 +01:00
# define NEWLINE "\r\n"
# define WHITESPACE "\n\r \t"
2009-09-02 00:34:27 +02:00
static pa_strlist * recorded_env = NULL ;
2013-09-20 10:10:50 +02:00
# ifdef OS_IS_WIN32
static fd_set nonblocking_fds ;
# endif
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2011-02-27 23:09:52 +01:00
/* Returns the directory of the current DLL, with '/bin/' removed if it is the last component */
char * pa_win32_get_toplevel ( HANDLE handle ) {
static char * toplevel = NULL ;
2006-01-10 17:51:06 +00:00
2011-02-27 23:09:52 +01:00
if ( ! toplevel ) {
char library_path [ MAX_PATH ] ;
char * p ;
2006-01-10 17:51:06 +00:00
2011-02-27 23:09:52 +01:00
if ( ! GetModuleFileName ( handle , library_path , MAX_PATH ) )
return NULL ;
2008-08-09 03:46:46 +02:00
2011-02-27 23:09:52 +01:00
toplevel = pa_xstrdup ( library_path ) ;
2006-01-10 17:51:06 +00:00
2011-02-27 23:09:52 +01:00
p = strrchr ( toplevel , PA_PATH_SEP_CHAR ) ;
if ( p )
* p = ' \0 ' ;
2006-01-10 17:51:06 +00:00
2011-02-27 23:09:52 +01:00
p = strrchr ( toplevel , PA_PATH_SEP_CHAR ) ;
2012-06-06 01:28:14 +05:30
if ( p & & pa_streq ( p + 1 , " bin " ) )
2011-02-27 23:09:52 +01:00
* p = ' \0 ' ;
}
2006-01-10 17:51:06 +00:00
2011-02-27 23:09:52 +01:00
return toplevel ;
2006-01-10 17:51:06 +00:00
}
# endif
2004-11-11 21:18:33 +00:00
2013-09-20 10:10:50 +02:00
static void set_nonblock ( int fd , bool nonblock ) {
2007-10-28 19:13:50 +00:00
2006-01-10 17:51:06 +00:00
# ifdef O_NONBLOCK
2013-09-20 10:10:50 +02:00
int v , nv ;
2007-10-28 19:13:50 +00:00
pa_assert ( fd > = 0 ) ;
pa_assert_se ( ( v = fcntl ( fd , F_GETFL ) ) > = 0 ) ;
2013-09-20 10:10:50 +02:00
if ( nonblock )
nv = v | O_NONBLOCK ;
else
nv = v & ~ O_NONBLOCK ;
if ( v ! = nv )
2007-10-28 19:13:50 +00:00
pa_assert_se ( fcntl ( fd , F_SETFL , v | O_NONBLOCK ) > = 0 ) ;
2004-06-23 23:17:30 +00:00
2006-01-10 17:51:06 +00:00
# elif defined(OS_IS_WIN32)
2013-09-20 10:10:50 +02:00
u_long arg ;
if ( nonblock )
arg = 1 ;
else
arg = 0 ;
2006-01-10 17:51:06 +00:00
if ( ioctlsocket ( fd , FIONBIO , & arg ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_assert_se ( WSAGetLastError ( ) = = WSAENOTSOCK ) ;
pa_log_warn ( " Only sockets can be made non-blocking! " ) ;
2013-09-20 10:10:50 +02:00
return ;
2006-01-10 17:51:06 +00:00
}
2013-09-20 10:10:50 +02:00
/* There is no method to query status, so we remember all fds */
if ( nonblock )
FD_SET ( fd , & nonblocking_fds ) ;
else
FD_CLR ( fd , & nonblocking_fds ) ;
2006-01-10 17:51:06 +00:00
# else
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Non-blocking I/O not supported.! " ) ;
2006-01-10 17:51:06 +00:00
# endif
2007-10-28 19:13:50 +00:00
}
2013-09-20 10:10:50 +02:00
/** Make a file descriptor nonblock. Doesn't do any error checking */
void pa_make_fd_nonblock ( int fd ) {
set_nonblock ( fd , true ) ;
}
/** Make a file descriptor blocking. Doesn't do any error checking */
void pa_make_fd_block ( int fd ) {
set_nonblock ( fd , false ) ;
}
/** Query if a file descriptor is non-blocking */
bool pa_is_fd_nonblock ( int fd ) {
# ifdef O_NONBLOCK
int v ;
pa_assert ( fd > = 0 ) ;
pa_assert_se ( ( v = fcntl ( fd , F_GETFL ) ) > = 0 ) ;
return ! ! ( v & O_NONBLOCK ) ;
# elif defined(OS_IS_WIN32)
return ! ! FD_ISSET ( fd , & nonblocking_fds ) ;
# else
return false ;
# endif
}
2007-10-28 19:13:50 +00:00
/* Set the FD_CLOEXEC flag for a fd */
void pa_make_fd_cloexec ( int fd ) {
# ifdef FD_CLOEXEC
int v ;
pa_assert ( fd > = 0 ) ;
pa_assert_se ( ( v = fcntl ( fd , F_GETFD , 0 ) ) > = 0 ) ;
if ( ! ( v & FD_CLOEXEC ) )
pa_assert_se ( fcntl ( fd , F_SETFD , v | FD_CLOEXEC ) > = 0 ) ;
# endif
2004-06-23 23:17:30 +00:00
}
2012-07-03 08:49:26 +05:30
/** Creates a directory securely. Will create parent directories recursively if
* required . This will not update permissions on parent directories if they
* already exist , however . */
2013-06-27 19:28:09 +02:00
int pa_make_secure_dir ( const char * dir , mode_t m , uid_t uid , gid_t gid , bool update_perms ) {
2004-06-29 16:48:37 +00:00
struct stat st ;
2011-06-23 22:21:03 +02:00
int r , saved_errno ;
2013-06-27 19:28:09 +02:00
bool retry = true ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( dir ) ;
2004-06-29 16:48:37 +00:00
2012-06-11 13:12:50 +05:30
again :
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2006-07-19 17:44:19 +00:00
r = mkdir ( dir ) ;
2006-01-10 17:51:06 +00:00
# else
2011-03-02 12:41:26 +01:00
{
2006-07-19 17:44:19 +00:00
mode_t u ;
2008-07-30 14:21:11 +02:00
u = umask ( ( ~ m ) & 0777 ) ;
2006-07-19 17:44:19 +00:00
r = mkdir ( dir , m ) ;
umask ( u ) ;
2011-03-02 12:41:26 +01:00
}
2006-01-10 17:51:06 +00:00
# endif
2007-01-04 13:43:45 +00:00
2012-06-11 13:12:50 +05:30
if ( r < 0 & & errno = = ENOENT & & retry ) {
/* If a parent directory in the path doesn't exist, try to create that
* first , then try again . */
2013-06-27 19:28:09 +02:00
pa_make_secure_parent_dir ( dir , m , uid , gid , false ) ;
retry = false ;
2012-06-11 13:12:50 +05:30
goto again ;
}
2006-07-19 17:44:19 +00:00
if ( r < 0 & & errno ! = EEXIST )
return - 1 ;
2006-01-10 17:51:06 +00:00
2011-01-06 00:51:33 +01:00
# if defined(HAVE_FSTAT) && !defined(OS_IS_WIN32)
2011-06-23 22:21:03 +02:00
{
int fd ;
2010-03-02 21:33:34 -08:00
if ( ( fd = open ( dir ,
# ifdef O_CLOEXEC
O_CLOEXEC |
# endif
# ifdef O_NOCTTY
O_NOCTTY |
# endif
# ifdef O_NOFOLLOW
O_NOFOLLOW |
# endif
O_RDONLY ) ) < 0 )
goto fail ;
if ( fstat ( fd , & st ) < 0 ) {
pa_assert_se ( pa_close ( fd ) > = 0 ) ;
goto fail ;
}
if ( ! S_ISDIR ( st . st_mode ) ) {
pa_assert_se ( pa_close ( fd ) > = 0 ) ;
errno = EEXIST ;
goto fail ;
}
2013-12-16 16:35:50 +01:00
if ( ! update_perms ) {
pa_assert_se ( pa_close ( fd ) > = 0 ) ;
2012-07-03 08:49:26 +05:30
return 0 ;
2013-12-16 16:35:50 +01:00
}
2012-07-03 08:49:26 +05:30
2010-03-02 21:33:34 -08:00
# ifdef HAVE_FCHOWN
2011-03-19 13:59:15 +01:00
if ( uid = = ( uid_t ) - 1 )
2006-07-20 00:21:50 +00:00
uid = getuid ( ) ;
2011-03-19 13:59:15 +01:00
if ( gid = = ( gid_t ) - 1 )
2006-07-20 00:21:50 +00:00
gid = getgid ( ) ;
2014-04-25 13:00:00 +03:00
if ( ( ( st . st_uid ! = uid ) | | ( st . st_gid ! = gid ) ) & & fchown ( fd , uid , gid ) < 0 ) {
2013-12-16 16:35:50 +01:00
pa_assert_se ( pa_close ( fd ) > = 0 ) ;
2011-03-19 13:59:15 +01:00
goto fail ;
2013-12-16 16:35:50 +01:00
}
2006-04-24 15:07:09 +00:00
# endif
2007-01-04 13:43:45 +00:00
2010-03-02 21:33:34 -08:00
# ifdef HAVE_FCHMOD
2015-10-10 20:11:21 +01:00
if ( ( st . st_mode & 07777 ) ! = m & & fchmod ( fd , m ) < 0 ) {
core-util: Remove redundant check of directory permissions
Initially (in commit ef422fa4ae626e9638ca70d1c56f27e701dd69c2),
pa_make_secure_dir followed a simple principle: "make a directory, or,
if it exists, check that it is suitable". Later this evolved into "make
a directory, or, if it exists, ensure that it is suitable". But the
check remained.
The check is now neither sufficient nor necessary. On POSIX-compliant
systems, the fstat results being checked are actually post-conditions of
fchmod and fchown. And on systems implementing POSIX ACLs, fstat only
reflects a part of the information relevant to the security of the
directory permissions, so PulseAudio could accept an existing insecure
directory anyway.
Also, the check still fires on non-POSIX-compliant filesystems like CIFS.
As a user cannot do anything to fix it, just accept insecure permissions
in this case.
2014-04-20 21:58:19 +06:00
pa_assert_se ( pa_close ( fd ) > = 0 ) ;
goto fail ;
} ;
2010-03-02 21:33:34 -08:00
# endif
pa_assert_se ( pa_close ( fd ) > = 0 ) ;
2011-06-23 22:21:03 +02:00
}
2006-01-10 17:51:06 +00:00
# else
core-util: Remove redundant check of directory permissions
Initially (in commit ef422fa4ae626e9638ca70d1c56f27e701dd69c2),
pa_make_secure_dir followed a simple principle: "make a directory, or,
if it exists, check that it is suitable". Later this evolved into "make
a directory, or, if it exists, ensure that it is suitable". But the
check remained.
The check is now neither sufficient nor necessary. On POSIX-compliant
systems, the fstat results being checked are actually post-conditions of
fchmod and fchown. And on systems implementing POSIX ACLs, fstat only
reflects a part of the information relevant to the security of the
directory permissions, so PulseAudio could accept an existing insecure
directory anyway.
Also, the check still fires on non-POSIX-compliant filesystems like CIFS.
As a user cannot do anything to fix it, just accept insecure permissions
in this case.
2014-04-20 21:58:19 +06:00
pa_log_warn ( " Secure directory creation not supported on this platform. " ) ;
2006-01-10 17:51:06 +00:00
# endif
2007-01-04 13:43:45 +00:00
2004-06-29 16:48:37 +00:00
return 0 ;
2007-01-04 13:43:45 +00:00
2004-06-29 16:48:37 +00:00
fail :
2008-08-09 03:46:46 +02:00
saved_errno = errno ;
2004-06-29 16:48:37 +00:00
rmdir ( dir ) ;
2008-08-09 03:46:46 +02:00
errno = saved_errno ;
2004-06-29 16:48:37 +00:00
return - 1 ;
}
2004-06-30 00:00:52 +00:00
2006-04-22 20:05:01 +00:00
/* Return a newly allocated sting containing the parent directory of the specified file */
char * pa_parent_dir ( const char * fn ) {
2004-11-11 21:18:33 +00:00
char * slash , * dir = pa_xstrdup ( fn ) ;
2006-01-10 17:51:06 +00:00
2006-07-19 17:44:19 +00:00
if ( ( slash = ( char * ) pa_path_get_filename ( dir ) ) = = dir ) {
pa_xfree ( dir ) ;
2008-08-09 03:46:46 +02:00
errno = ENOENT ;
2006-04-22 20:05:01 +00:00
return NULL ;
2006-07-19 17:44:19 +00:00
}
2006-04-22 20:05:01 +00:00
2006-01-10 17:51:06 +00:00
* ( slash - 1 ) = 0 ;
2006-04-22 20:05:01 +00:00
return dir ;
}
/* Creates a the parent directory of the specified path securely */
2013-06-27 19:28:09 +02:00
int pa_make_secure_parent_dir ( const char * fn , mode_t m , uid_t uid , gid_t gid , bool update_perms ) {
2006-04-22 20:05:01 +00:00
int ret = - 1 ;
char * dir ;
if ( ! ( dir = pa_parent_dir ( fn ) ) )
goto finish ;
2007-01-04 13:43:45 +00:00
2012-07-03 08:49:26 +05:30
if ( pa_make_secure_dir ( dir , m , uid , gid , update_perms ) < 0 )
2004-11-11 21:18:33 +00:00
goto finish ;
ret = 0 ;
2007-01-04 13:43:45 +00:00
2004-11-11 21:18:33 +00:00
finish :
pa_xfree ( dir ) ;
return ret ;
}
2006-07-14 22:42:01 +00:00
/** Platform independent read function. Necessary since not all
* systems treat all file descriptors equal . If type is
* non - NULL it is used to cache the type of the fd . This is
* useful for making sure that only a single syscall is executed per
* function call . The variable pointed to should be initialized to 0
* by the caller . */
ssize_t pa_read ( int fd , void * buf , size_t count , int * type ) {
2006-05-11 11:08:58 +00:00
# ifdef OS_IS_WIN32
2006-07-14 22:42:01 +00:00
if ( ! type | | * type = = 0 ) {
ssize_t r ;
2007-01-04 13:43:45 +00:00
2006-07-14 22:42:01 +00:00
if ( ( r = recv ( fd , buf , count , 0 ) ) > = 0 )
return r ;
2013-09-20 10:13:41 +02:00
if ( WSAGetLastError ( ) ! = WSAENOTSOCK ) {
errno = WSAGetLastError ( ) ;
2006-05-11 11:08:58 +00:00
return r ;
}
2006-07-14 22:42:01 +00:00
if ( type )
* type = 1 ;
2006-05-11 11:08:58 +00:00
}
# endif
2007-01-04 13:43:45 +00:00
2009-04-18 23:21:05 +02:00
for ( ; ; ) {
ssize_t r ;
if ( ( r = read ( fd , buf , count ) ) < 0 )
if ( errno = = EINTR )
continue ;
return r ;
}
2006-05-11 11:08:58 +00:00
}
/** Similar to pa_read(), but handles writes */
2006-07-14 22:42:01 +00:00
ssize_t pa_write ( int fd , const void * buf , size_t count , int * type ) {
if ( ! type | | * type = = 0 ) {
ssize_t r ;
2006-05-11 11:08:58 +00:00
2009-04-18 23:21:05 +02:00
for ( ; ; ) {
if ( ( r = send ( fd , buf , count , MSG_NOSIGNAL ) ) < 0 ) {
if ( errno = = EINTR )
continue ;
break ;
}
2006-07-14 22:42:01 +00:00
return r ;
2009-04-18 23:21:05 +02:00
}
2007-01-04 13:43:45 +00:00
2006-05-11 11:08:58 +00:00
# ifdef OS_IS_WIN32
2013-09-20 10:13:41 +02:00
if ( WSAGetLastError ( ) ! = WSAENOTSOCK ) {
errno = WSAGetLastError ( ) ;
2006-05-11 11:08:58 +00:00
return r ;
}
2006-07-14 22:42:01 +00:00
# else
if ( errno ! = ENOTSOCK )
return r ;
2006-05-11 11:08:58 +00:00
# endif
2006-07-14 22:42:01 +00:00
if ( type )
* type = 1 ;
}
2009-04-18 23:21:05 +02:00
for ( ; ; ) {
ssize_t r ;
if ( ( r = write ( fd , buf , count ) ) < 0 )
if ( errno = = EINTR )
continue ;
return r ;
}
2006-05-11 11:08:58 +00:00
}
2004-11-04 18:57:31 +00:00
/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
2009-04-01 12:35:44 +02:00
* unless EOF is reached or an error occurred */
2006-07-14 22:42:01 +00:00
ssize_t pa_loop_read ( int fd , void * data , size_t size , int * type ) {
2004-07-06 00:08:44 +00:00
ssize_t ret = 0 ;
2006-07-14 22:42:01 +00:00
int _type ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( fd > = 0 ) ;
pa_assert ( data ) ;
pa_assert ( size ) ;
2006-07-14 22:42:01 +00:00
if ( ! type ) {
_type = 0 ;
type = & _type ;
}
2004-07-06 00:08:44 +00:00
while ( size > 0 ) {
ssize_t r ;
2006-07-14 22:42:01 +00:00
if ( ( r = pa_read ( fd , data , size , type ) ) < 0 )
2004-07-06 00:08:44 +00:00
return r ;
if ( r = = 0 )
break ;
2007-01-04 13:43:45 +00:00
2004-07-06 00:08:44 +00:00
ret + = r ;
2004-09-01 00:23:51 +00:00
data = ( uint8_t * ) data + r ;
2008-08-19 22:39:54 +02:00
size - = ( size_t ) r ;
2004-07-06 00:08:44 +00:00
}
return ret ;
}
2004-11-04 18:57:31 +00:00
/** Similar to pa_loop_read(), but wraps write() */
2006-07-14 22:42:01 +00:00
ssize_t pa_loop_write ( int fd , const void * data , size_t size , int * type ) {
2004-07-06 00:08:44 +00:00
ssize_t ret = 0 ;
2006-07-14 22:42:01 +00:00
int _type ;
2007-10-28 19:13:50 +00:00
pa_assert ( fd > = 0 ) ;
pa_assert ( data ) ;
pa_assert ( size ) ;
2006-07-14 22:42:01 +00:00
if ( ! type ) {
_type = 0 ;
type = & _type ;
}
2004-07-06 00:08:44 +00:00
while ( size > 0 ) {
ssize_t r ;
2006-07-14 22:42:01 +00:00
if ( ( r = pa_write ( fd , data , size , type ) ) < 0 )
2004-07-06 00:08:44 +00:00
return r ;
if ( r = = 0 )
break ;
2007-01-04 13:43:45 +00:00
2004-07-06 00:08:44 +00:00
ret + = r ;
2006-01-11 01:17:39 +00:00
data = ( const uint8_t * ) data + r ;
2008-08-19 22:39:54 +02:00
size - = ( size_t ) r ;
2004-07-06 00:08:44 +00:00
}
return ret ;
}
2013-09-20 10:10:50 +02:00
/** Platform independent close function. Necessary since not all
2007-02-14 12:13:49 +00:00
* systems treat all file descriptors equal . */
2007-10-28 19:13:50 +00:00
int pa_close ( int fd ) {
2007-02-14 12:13:49 +00:00
# ifdef OS_IS_WIN32
int ret ;
2013-09-20 10:10:50 +02:00
FD_CLR ( fd , & nonblocking_fds ) ;
2007-10-28 19:13:50 +00:00
if ( ( ret = closesocket ( fd ) ) = = 0 )
2007-02-14 12:13:49 +00:00
return 0 ;
if ( WSAGetLastError ( ) ! = WSAENOTSOCK ) {
errno = WSAGetLastError ( ) ;
return ret ;
}
# endif
2008-08-26 15:44:55 +02:00
for ( ; ; ) {
int r ;
2009-04-18 23:21:05 +02:00
if ( ( r = close ( fd ) ) < 0 )
if ( errno = = EINTR )
continue ;
2008-08-26 15:44:55 +02:00
2009-04-18 23:21:05 +02:00
return r ;
2008-08-26 15:44:55 +02:00
}
2007-02-14 12:13:49 +00:00
}
2004-11-04 18:57:31 +00:00
/* Print a warning messages in case that the given signal is not
* blocked or trapped */
2004-09-15 13:03:25 +00:00
void pa_check_signal_is_blocked ( int sig ) {
2006-01-10 17:51:06 +00:00
# ifdef HAVE_SIGACTION
2004-07-07 00:22:46 +00:00
struct sigaction sa ;
2004-08-10 13:00:12 +00:00
sigset_t set ;
2004-11-04 18:57:31 +00:00
/* If POSIX threads are supported use thread-aware
* pthread_sigmask ( ) function , to check if the signal is
* blocked . Otherwise fall back to sigprocmask ( ) */
2007-01-04 13:43:45 +00:00
# ifdef HAVE_PTHREAD
2004-08-10 13:00:12 +00:00
if ( pthread_sigmask ( SIG_SETMASK , NULL , & set ) < 0 ) {
2004-08-17 17:56:09 +00:00
# endif
2004-08-10 13:00:12 +00:00
if ( sigprocmask ( SIG_SETMASK , NULL , & set ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " sigprocmask(): %s " , pa_cstrerror ( errno ) ) ;
2004-08-10 13:00:12 +00:00
return ;
}
2004-08-17 17:56:09 +00:00
# ifdef HAVE_PTHREAD
2004-08-10 13:00:12 +00:00
}
2004-08-17 17:56:09 +00:00
# endif
2004-07-07 00:22:46 +00:00
2004-09-15 13:03:25 +00:00
if ( sigismember ( & set , sig ) )
2004-08-10 13:00:12 +00:00
return ;
2004-11-04 18:57:31 +00:00
/* Check whether the signal is trapped */
2007-01-04 13:43:45 +00:00
2004-09-15 13:03:25 +00:00
if ( sigaction ( sig , NULL , & sa ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " sigaction(): %s " , pa_cstrerror ( errno ) ) ;
2004-07-07 00:22:46 +00:00
return ;
}
2007-01-04 13:43:45 +00:00
2004-08-10 13:00:12 +00:00
if ( sa . sa_handler ! = SIG_DFL )
return ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_log_warn ( " %s is not trapped. This might cause malfunction! " , pa_sig2str ( sig ) ) ;
2006-01-10 17:51:06 +00:00
# else /* HAVE_SIGACTION */
2007-10-28 19:13:50 +00:00
pa_log_warn ( " %s might not be trapped. This might cause malfunction! " , pa_sig2str ( sig ) ) ;
2006-01-10 17:51:06 +00:00
# endif
2004-07-07 00:22:46 +00:00
}
2004-07-10 20:56:38 +00:00
2004-11-04 18:57:31 +00:00
/* The following function is based on an example from the GNU libc
* documentation . This function is similar to GNU ' s asprintf ( ) . */
2004-07-10 20:56:38 +00:00
char * pa_sprintf_malloc ( const char * format , . . . ) {
2011-03-12 19:45:02 +01:00
size_t size = 100 ;
2004-07-10 20:56:38 +00:00
char * c = NULL ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( format ) ;
2007-01-04 13:43:45 +00:00
2004-07-10 20:56:38 +00:00
for ( ; ; ) {
int r ;
va_list ap ;
2004-08-04 16:39:30 +00:00
c = pa_xrealloc ( c , size ) ;
2004-07-10 20:56:38 +00:00
va_start ( ap , format ) ;
r = vsnprintf ( c , size , format , ap ) ;
va_end ( ap ) ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
c [ size - 1 ] = 0 ;
2008-08-19 22:39:54 +02:00
if ( r > - 1 & & ( size_t ) r < size )
2004-07-10 20:56:38 +00:00
return c ;
if ( r > - 1 ) /* glibc 2.1 */
2008-08-19 22:39:54 +02:00
size = ( size_t ) r + 1 ;
2004-07-10 20:56:38 +00:00
else /* glibc 2.0 */
size * = 2 ;
}
}
2004-08-10 13:00:12 +00:00
2004-11-04 18:57:31 +00:00
/* Same as the previous function, but use a va_list instead of an
* ellipsis */
2004-09-05 00:03:16 +00:00
char * pa_vsprintf_malloc ( const char * format , va_list ap ) {
2011-03-12 19:45:02 +01:00
size_t size = 100 ;
2004-09-05 00:03:16 +00:00
char * c = NULL ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( format ) ;
2007-01-04 13:43:45 +00:00
2004-09-05 00:03:16 +00:00
for ( ; ; ) {
int r ;
2006-06-13 15:54:11 +00:00
va_list aq ;
2004-09-05 00:03:16 +00:00
c = pa_xrealloc ( c , size ) ;
2006-06-13 15:54:11 +00:00
2007-10-28 19:13:50 +00:00
va_copy ( aq , ap ) ;
r = vsnprintf ( c , size , format , aq ) ;
2006-06-13 15:54:11 +00:00
va_end ( aq ) ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
c [ size - 1 ] = 0 ;
2008-08-19 22:39:54 +02:00
if ( r > - 1 & & ( size_t ) r < size )
2004-09-05 00:03:16 +00:00
return c ;
if ( r > - 1 ) /* glibc 2.1 */
2008-08-19 22:39:54 +02:00
size = ( size_t ) r + 1 ;
2004-09-05 00:03:16 +00:00
else /* glibc 2.0 */
size * = 2 ;
}
}
2004-11-04 18:57:31 +00:00
/* Similar to OpenBSD's strlcpy() function */
char * pa_strlcpy ( char * b , const char * s , size_t l ) {
2009-08-01 02:01:58 +02:00
size_t k ;
2007-10-28 19:13:50 +00:00
pa_assert ( b ) ;
pa_assert ( s ) ;
pa_assert ( l > 0 ) ;
2004-11-04 18:57:31 +00:00
2009-08-01 02:01:58 +02:00
k = strlen ( s ) ;
if ( k > l - 1 )
k = l - 1 ;
memcpy ( b , s , k ) ;
b [ k ] = 0 ;
2004-11-04 18:57:31 +00:00
return b ;
}
2011-03-02 12:41:23 +01:00
# ifdef _POSIX_PRIORITY_SCHEDULING
2009-06-19 04:19:08 +02:00
static int set_scheduler ( int rtprio ) {
2011-01-06 00:51:33 +01:00
# ifdef HAVE_SCHED_H
2009-06-19 04:19:08 +02:00
struct sched_param sp ;
# ifdef HAVE_DBUS
2009-12-10 15:43:58 +08:00
int r ;
2013-06-09 18:47:05 +02:00
long long rttime ;
2013-12-10 13:27:52 -05:00
# ifdef RLIMIT_RTTIME
2013-06-09 18:47:05 +02:00
struct rlimit rl ;
# endif
2009-06-19 04:19:08 +02:00
DBusError error ;
DBusConnection * bus ;
dbus_error_init ( & error ) ;
# endif
pa_zero ( sp ) ;
sp . sched_priority = rtprio ;
# ifdef SCHED_RESET_ON_FORK
2009-09-08 23:46:23 +02:00
if ( pthread_setschedparam ( pthread_self ( ) , SCHED_RR | SCHED_RESET_ON_FORK , & sp ) = = 0 ) {
2009-06-19 04:19:08 +02:00
pa_log_debug ( " SCHED_RR|SCHED_RESET_ON_FORK worked. " ) ;
return 0 ;
}
# endif
2009-09-08 23:46:23 +02:00
if ( pthread_setschedparam ( pthread_self ( ) , SCHED_RR , & sp ) = = 0 ) {
2009-06-19 04:19:08 +02:00
pa_log_debug ( " SCHED_RR worked. " ) ;
return 0 ;
}
2011-01-06 00:51:33 +01:00
# endif /* HAVE_SCHED_H */
2009-06-19 04:19:08 +02:00
# ifdef HAVE_DBUS
/* Try to talk to RealtimeKit */
2011-05-06 22:54:48 +02:00
if ( ! ( bus = dbus_bus_get_private ( DBUS_BUS_SYSTEM , & error ) ) ) {
2016-08-16 07:03:25 +02:00
pa_log ( " Failed to connect to system bus: %s " , error . message ) ;
2009-06-19 04:19:08 +02:00
dbus_error_free ( & error ) ;
errno = - EIO ;
return - 1 ;
}
2009-09-02 04:03:18 +02:00
/* We need to disable exit on disconnect because otherwise
* dbus_shutdown will kill us . See
* https : //bugs.freedesktop.org/show_bug.cgi?id=16924 */
2013-06-28 01:08:31 +02:00
dbus_connection_set_exit_on_disconnect ( bus , FALSE ) ;
2009-09-02 04:03:18 +02:00
2013-06-09 18:47:05 +02:00
rttime = rtkit_get_rttime_usec_max ( bus ) ;
if ( rttime > = 0 ) {
2013-12-10 13:27:52 -05:00
# ifdef RLIMIT_RTTIME
2013-06-09 18:47:05 +02:00
r = getrlimit ( RLIMIT_RTTIME , & rl ) ;
2009-06-19 04:19:08 +02:00
2013-06-09 18:47:05 +02:00
if ( r > = 0 & & ( long long ) rl . rlim_max > rttime ) {
2016-08-16 07:03:25 +02:00
pa_log_info ( " Clamping rlimit-rttime to %lld for RealtimeKit " , rttime ) ;
2013-06-09 18:47:05 +02:00
rl . rlim_cur = rl . rlim_max = rttime ;
r = setrlimit ( RLIMIT_RTTIME , & rl ) ;
if ( r < 0 )
2013-06-23 10:23:10 +03:00
pa_log ( " setrlimit() failed: %s " , pa_cstrerror ( errno ) ) ;
2013-06-09 18:47:05 +02:00
}
# endif
r = rtkit_make_realtime ( bus , 0 , rtprio ) ;
dbus_connection_close ( bus ) ;
dbus_connection_unref ( bus ) ;
if ( r > = 0 ) {
pa_log_debug ( " RealtimeKit worked. " ) ;
return 0 ;
}
errno = - r ;
} else {
dbus_connection_close ( bus ) ;
dbus_connection_unref ( bus ) ;
errno = - rttime ;
2009-06-19 04:19:08 +02:00
}
# else
2009-12-10 15:43:58 +08:00
errno = 0 ;
2009-06-19 04:19:08 +02:00
# endif
return - 1 ;
}
2011-03-02 12:41:23 +01:00
# endif
2009-06-19 04:19:08 +02:00
2007-11-01 02:58:26 +00:00
/* Make the current thread a realtime thread, and acquire the highest
* rtprio we can get that is less or equal the specified parameter . If
* the thread is already realtime , don ' t do anything . */
int pa_make_realtime ( int rtprio ) {
2004-09-01 00:23:51 +00:00
2011-03-20 17:57:49 +01:00
# if defined(OS_IS_DARWIN)
struct thread_time_constraint_policy ttcpolicy ;
uint64_t freq = 0 ;
size_t size = sizeof ( freq ) ;
int ret ;
ret = sysctlbyname ( " hw.cpufrequency " , & freq , & size , NULL , 0 ) ;
if ( ret < 0 ) {
pa_log_info ( " Unable to read CPU frequency, acquisition of real-time scheduling failed. " ) ;
return - 1 ;
}
pa_log_debug ( " sysctl for hw.cpufrequency: %llu " , freq ) ;
/* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */
ttcpolicy . period = freq / 160 ;
ttcpolicy . computation = freq / 3300 ;
ttcpolicy . constraint = freq / 2200 ;
ttcpolicy . preemptible = 1 ;
ret = thread_policy_set ( mach_thread_self ( ) ,
THREAD_TIME_CONSTRAINT_POLICY ,
( thread_policy_t ) & ttcpolicy ,
THREAD_TIME_CONSTRAINT_POLICY_COUNT ) ;
if ( ret ) {
pa_log_info ( " Unable to set real-time thread priority (%08x). " , ret ) ;
return - 1 ;
}
pa_log_info ( " Successfully acquired real-time thread priority. " ) ;
return 0 ;
2011-06-23 22:21:03 +02:00
# elif defined(_POSIX_PRIORITY_SCHEDULING)
2009-06-19 04:45:19 +02:00
int p ;
2007-10-28 19:13:50 +00:00
2009-06-19 04:19:08 +02:00
if ( set_scheduler ( rtprio ) > = 0 ) {
pa_log_info ( " Successfully enabled SCHED_RR scheduling for thread, with priority %i. " , rtprio ) ;
return 0 ;
}
2007-11-01 02:58:26 +00:00
2009-06-19 04:45:19 +02:00
for ( p = rtprio - 1 ; p > = 1 ; p - - )
2011-01-20 16:41:21 -07:00
if ( set_scheduler ( p ) > = 0 ) {
2009-06-19 04:45:19 +02:00
pa_log_info ( " Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i. " , p , rtprio ) ;
2009-06-19 04:19:08 +02:00
return 0 ;
2007-11-01 02:58:26 +00:00
}
2011-01-06 02:10:45 +01:00
# elif defined(OS_IS_WIN32)
/* Windows only allows realtime scheduling to be set on a per process basis.
* Therefore , instead of making the thread realtime , just give it the highest non - realtime priority . */
2011-02-27 23:09:52 +01:00
if ( SetThreadPriority ( GetCurrentThread ( ) , THREAD_PRIORITY_TIME_CRITICAL ) ) {
2011-01-06 02:10:45 +01:00
pa_log_info ( " Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread. " ) ;
return 0 ;
}
2007-10-28 19:13:50 +00:00
2011-01-06 02:10:45 +01:00
pa_log_warn ( " SetThreadPriority() failed: 0x%08X " , GetLastError ( ) ) ;
errno = EPERM ;
2007-11-01 02:58:26 +00:00
# else
2008-08-09 03:46:46 +02:00
errno = ENOTSUP ;
2007-05-25 20:35:30 +00:00
# endif
2011-01-06 02:10:45 +01:00
pa_log_info ( " Failed to acquire real-time scheduling: %s " , pa_cstrerror ( errno ) ) ;
return - 1 ;
2007-10-28 19:13:50 +00:00
}
2011-03-02 12:41:23 +01:00
# ifdef HAVE_SYS_RESOURCE_H
2009-06-19 04:45:19 +02:00
static int set_nice ( int nice_level ) {
# ifdef HAVE_DBUS
DBusError error ;
DBusConnection * bus ;
int r ;
2008-05-15 23:34:41 +00:00
2009-06-19 04:45:19 +02:00
dbus_error_init ( & error ) ;
2008-05-15 23:34:41 +00:00
# endif
2011-01-06 00:51:33 +01:00
# ifdef HAVE_SYS_RESOURCE_H
2009-06-19 04:45:19 +02:00
if ( setpriority ( PRIO_PROCESS , 0 , nice_level ) > = 0 ) {
pa_log_debug ( " setpriority() worked. " ) ;
return 0 ;
2008-05-15 23:34:41 +00:00
}
2011-01-06 00:51:33 +01:00
# endif
2008-05-15 23:34:41 +00:00
2009-06-19 04:45:19 +02:00
# ifdef HAVE_DBUS
/* Try to talk to RealtimeKit */
2008-05-15 23:34:41 +00:00
2015-03-12 13:42:46 +01:00
if ( ! ( bus = dbus_bus_get_private ( DBUS_BUS_SYSTEM , & error ) ) ) {
2016-08-16 07:03:25 +02:00
pa_log ( " Failed to connect to system bus: %s " , error . message ) ;
2009-06-19 04:45:19 +02:00
dbus_error_free ( & error ) ;
errno = - EIO ;
return - 1 ;
2008-05-15 23:34:41 +00:00
}
2009-09-02 04:03:18 +02:00
/* We need to disable exit on disconnect because otherwise
* dbus_shutdown will kill us . See
* https : //bugs.freedesktop.org/show_bug.cgi?id=16924 */
2013-06-28 01:08:31 +02:00
dbus_connection_set_exit_on_disconnect ( bus , FALSE ) ;
2009-09-02 04:03:18 +02:00
2009-06-19 04:45:19 +02:00
r = rtkit_make_high_priority ( bus , 0 , nice_level ) ;
2015-03-12 13:42:46 +01:00
dbus_connection_close ( bus ) ;
2009-06-19 04:45:19 +02:00
dbus_connection_unref ( bus ) ;
2008-05-15 23:34:41 +00:00
2009-06-19 04:45:19 +02:00
if ( r > = 0 ) {
pa_log_debug ( " RealtimeKit worked. " ) ;
return 0 ;
2008-05-15 23:34:41 +00:00
}
2009-06-19 04:45:19 +02:00
errno = - r ;
2008-05-15 23:34:41 +00:00
# endif
2009-06-19 04:45:19 +02:00
return - 1 ;
2008-05-15 23:34:41 +00:00
}
2011-03-02 12:41:23 +01:00
# endif
2008-05-15 23:34:41 +00:00
2007-11-01 02:58:26 +00:00
/* Raise the priority of the current process as much as possible that
* is < = the specified nice level . . */
int pa_raise_priority ( int nice_level ) {
2007-10-28 19:13:50 +00:00
2006-01-10 17:51:06 +00:00
# ifdef HAVE_SYS_RESOURCE_H
2009-06-19 04:45:19 +02:00
int n ;
2007-11-01 02:58:26 +00:00
2009-06-19 04:45:19 +02:00
if ( set_nice ( nice_level ) > = 0 ) {
pa_log_info ( " Successfully gained nice level %i. " , nice_level ) ;
return 0 ;
}
2007-11-01 02:58:26 +00:00
2009-06-19 04:45:19 +02:00
for ( n = nice_level + 1 ; n < 0 ; n + + )
2011-01-20 16:41:21 -07:00
if ( set_nice ( n ) > = 0 ) {
2009-06-19 04:45:19 +02:00
pa_log_info ( " Successfully acquired nice level %i, which is lower than the requested %i. " , n , nice_level ) ;
return 0 ;
2007-11-01 02:58:26 +00:00
}
2009-06-19 04:45:19 +02:00
pa_log_info ( " Failed to acquire high-priority scheduling: %s " , pa_cstrerror ( errno ) ) ;
return - 1 ;
2006-01-10 17:51:06 +00:00
# endif
2007-01-04 13:43:45 +00:00
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2007-11-01 02:58:26 +00:00
if ( nice_level < 0 ) {
if ( ! SetPriorityClass ( GetCurrentProcess ( ) , HIGH_PRIORITY_CLASS ) ) {
pa_log_warn ( " SetPriorityClass() failed: 0x%08X " , GetLastError ( ) ) ;
2008-08-09 03:46:46 +02:00
errno = EPERM ;
2009-06-19 04:45:19 +02:00
return - 1 ;
}
pa_log_info ( " Successfully gained high priority class. " ) ;
2007-11-01 02:58:26 +00:00
}
2006-01-10 17:51:06 +00:00
# endif
2007-11-01 02:58:26 +00:00
return 0 ;
2004-09-01 00:23:51 +00:00
}
2007-10-28 19:13:50 +00:00
/* Reset the priority to normal, inverting the changes made by
2007-11-01 02:58:26 +00:00
* pa_raise_priority ( ) and pa_make_realtime ( ) */
2004-09-01 00:23:51 +00:00
void pa_reset_priority ( void ) {
2006-01-10 17:51:06 +00:00
# ifdef HAVE_SYS_RESOURCE_H
2007-11-01 02:58:26 +00:00
struct sched_param sp ;
2004-09-01 00:23:51 +00:00
setpriority ( PRIO_PROCESS , 0 , 0 ) ;
2007-11-01 02:58:26 +00:00
2009-06-19 04:45:19 +02:00
pa_zero ( sp ) ;
pthread_setschedparam ( pthread_self ( ) , SCHED_OTHER , & sp ) ;
2007-11-01 02:58:26 +00:00
# endif
# ifdef OS_IS_WIN32
SetPriorityClass ( GetCurrentProcess ( ) , NORMAL_PRIORITY_CLASS ) ;
2006-01-10 17:51:06 +00:00
# endif
2004-09-01 00:23:51 +00:00
}
2004-09-01 22:36:49 +00:00
2009-02-18 21:57:16 +01:00
int pa_match ( const char * expr , const char * v ) {
2013-11-29 15:58:42 +01:00
# if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
2008-08-09 03:46:23 +02:00
int k ;
regex_t re ;
2008-08-09 16:12:50 +02:00
int r ;
2008-08-09 03:46:23 +02:00
if ( regcomp ( & re , expr , REG_NOSUB | REG_EXTENDED ) ! = 0 ) {
errno = EINVAL ;
return - 1 ;
}
if ( ( k = regexec ( & re , v , 0 , NULL , 0 ) ) = = 0 )
2008-08-09 16:12:50 +02:00
r = 1 ;
2008-08-09 03:46:23 +02:00
else if ( k = = REG_NOMATCH )
2008-08-09 16:12:50 +02:00
r = 0 ;
else
r = - 1 ;
2008-08-09 03:46:23 +02:00
2008-08-09 16:12:50 +02:00
regfree ( & re ) ;
if ( r < 0 )
errno = EINVAL ;
return r ;
2013-11-29 15:58:42 +01:00
# else
errno = ENOSYS ;
return - 1 ;
# endif
2008-08-09 03:46:23 +02:00
}
2004-11-04 21:27:12 +00:00
/* Try to parse a boolean string value.*/
2004-09-13 23:28:30 +00:00
int pa_parse_boolean ( const char * v ) {
2008-05-15 23:34:41 +00:00
pa_assert ( v ) ;
2007-01-04 13:43:45 +00:00
2011-08-24 18:24:46 +02:00
/* First we check language independent */
2012-06-13 11:15:03 +03:00
if ( pa_streq ( v , " 1 " ) | | ! strcasecmp ( v , " y " ) | | ! strcasecmp ( v , " t " )
| | ! strcasecmp ( v , " yes " ) | | ! strcasecmp ( v , " true " ) | | ! strcasecmp ( v , " on " ) )
2004-09-13 23:28:30 +00:00
return 1 ;
2012-06-13 11:15:03 +03:00
else if ( pa_streq ( v , " 0 " ) | | ! strcasecmp ( v , " n " ) | | ! strcasecmp ( v , " f " )
| | ! strcasecmp ( v , " no " ) | | ! strcasecmp ( v , " false " ) | | ! strcasecmp ( v , " off " ) )
2004-09-13 23:28:30 +00:00
return 0 ;
2011-01-06 00:51:33 +01:00
# ifdef HAVE_LANGINFO_H
2011-06-23 22:21:03 +02:00
{
const char * expr ;
2011-08-24 18:24:46 +02:00
/* And then we check language dependent */
2008-08-09 03:46:23 +02:00
if ( ( expr = nl_langinfo ( YESEXPR ) ) )
if ( expr [ 0 ] )
2009-09-08 23:46:23 +02:00
if ( pa_match ( expr , v ) > 0 )
2008-08-09 03:46:23 +02:00
return 1 ;
if ( ( expr = nl_langinfo ( NOEXPR ) ) )
if ( expr [ 0 ] )
2009-09-08 23:46:23 +02:00
if ( pa_match ( expr , v ) > 0 )
2008-08-09 03:46:23 +02:00
return 0 ;
2011-06-23 22:21:03 +02:00
}
2011-01-06 00:51:33 +01:00
# endif
2008-08-09 03:46:23 +02:00
errno = EINVAL ;
2004-09-13 23:28:30 +00:00
return - 1 ;
}
2004-09-14 17:52:11 +00:00
2012-10-24 17:29:45 -02:00
/* Try to parse a volume string to pa_volume_t. The allowed formats are:
* db , % and unsigned integer */
int pa_parse_volume ( const char * v , pa_volume_t * volume ) {
2015-04-27 14:34:03 +03:00
int len ;
2012-10-24 17:29:45 -02:00
uint32_t i ;
double d ;
char str [ 64 ] ;
pa_assert ( v ) ;
pa_assert ( volume ) ;
len = strlen ( v ) ;
if ( len > = 64 )
return - 1 ;
memcpy ( str , v , len + 1 ) ;
if ( str [ len - 1 ] = = ' % ' ) {
str [ len - 1 ] = ' \0 ' ;
2015-04-27 14:34:03 +03:00
if ( pa_atod ( str , & d ) < 0 )
return - 1 ;
d = d / 100 * PA_VOLUME_NORM ;
if ( d < 0 | | d > PA_VOLUME_MAX )
return - 1 ;
* volume = d ;
return 0 ;
}
if ( len > 2 & & ( str [ len - 1 ] = = ' b ' | | str [ len - 1 ] = = ' B ' ) & &
2012-10-24 17:29:45 -02:00
( str [ len - 2 ] = = ' d ' | | str [ len - 2 ] = = ' D ' ) ) {
str [ len - 2 ] = ' \0 ' ;
2015-04-27 14:34:03 +03:00
if ( pa_atod ( str , & d ) < 0 )
return - 1 ;
if ( d > pa_sw_volume_to_dB ( PA_VOLUME_MAX ) )
return - 1 ;
2012-10-24 17:29:45 -02:00
2015-04-27 14:34:03 +03:00
* volume = pa_sw_volume_from_dB ( d ) ;
return 0 ;
2012-10-24 17:29:45 -02:00
}
2015-04-27 14:34:03 +03:00
if ( pa_atou ( v , & i ) < 0 | | ! PA_VOLUME_IS_VALID ( i ) )
return - 1 ;
* volume = i ;
return 0 ;
2012-10-24 17:29:45 -02:00
}
2004-11-04 21:27:12 +00:00
/* Split the specified string wherever one of the strings in delimiter
* occurs . Each time it is called returns a newly allocated string
* with pa_xmalloc ( ) . The variable state points to , should be
2011-08-24 18:24:46 +02:00
* initialized to NULL before the first call . */
2004-09-14 17:52:11 +00:00
char * pa_split ( const char * c , const char * delimiter , const char * * state ) {
const char * current = * state ? * state : c ;
size_t l ;
if ( ! * current )
return NULL ;
2007-01-04 13:43:45 +00:00
2004-09-14 17:52:11 +00:00
l = strcspn ( current , delimiter ) ;
* state = current + l ;
if ( * * state )
2004-09-16 23:34:25 +00:00
( * state ) + + ;
2004-09-14 17:52:11 +00:00
return pa_xstrndup ( current , l ) ;
}
2004-09-15 13:03:25 +00:00
2012-06-13 16:31:59 +05:30
/* Split the specified string wherever one of the strings in delimiter
* occurs . Each time it is called returns a pointer to the substring within the
* string and the length in ' n ' . Note that the resultant string cannot be used
* as - is without the length parameter , since it is merely pointing to a point
* within the original string . The variable state points to , should be
* initialized to NULL before the first call . */
const char * pa_split_in_place ( const char * c , const char * delimiter , int * n , const char * * state ) {
const char * current = * state ? * state : c ;
size_t l ;
if ( ! * current )
return NULL ;
l = strcspn ( current , delimiter ) ;
* state = current + l ;
if ( * * state )
( * state ) + + ;
* n = l ;
return current ;
}
2004-11-04 21:27:12 +00:00
/* Split a string into words. Otherwise similar to pa_split(). */
2004-09-17 19:45:44 +00:00
char * pa_split_spaces ( const char * c , const char * * state ) {
const char * current = * state ? * state : c ;
size_t l ;
2004-11-11 21:18:33 +00:00
if ( ! * current | | * c = = 0 )
2004-09-17 19:45:44 +00:00
return NULL ;
current + = strspn ( current , WHITESPACE ) ;
l = strcspn ( current , WHITESPACE ) ;
* state = current + l ;
return pa_xstrndup ( current , l ) ;
}
2007-10-28 19:13:50 +00:00
PA_STATIC_TLS_DECLARE ( signame , pa_xfree ) ;
/* Return the name of an UNIX signal. Similar to Solaris sig2str() */
const char * pa_sig2str ( int sig ) {
char * t ;
if ( sig < = 0 )
goto fail ;
# ifdef NSIG
if ( sig > = NSIG )
goto fail ;
# endif
# ifdef HAVE_SIG2STR
{
char buf [ SIG2STR_MAX ] ;
if ( sig2str ( sig , buf ) = = 0 ) {
pa_xfree ( PA_STATIC_TLS_GET ( signame ) ) ;
t = pa_sprintf_malloc ( " SIG%s " , buf ) ;
PA_STATIC_TLS_SET ( signame , t ) ;
return t ;
}
}
# else
2011-02-27 23:09:52 +01:00
switch ( sig ) {
2007-10-28 19:13:50 +00:00
# ifdef SIGHUP
case SIGHUP : return " SIGHUP " ;
# endif
case SIGINT : return " SIGINT " ;
# ifdef SIGQUIT
case SIGQUIT : return " SIGQUIT " ;
# endif
case SIGILL : return " SIGULL " ;
# ifdef SIGTRAP
case SIGTRAP : return " SIGTRAP " ;
# endif
case SIGABRT : return " SIGABRT " ;
# ifdef SIGBUS
case SIGBUS : return " SIGBUS " ;
# endif
case SIGFPE : return " SIGFPE " ;
# ifdef SIGKILL
case SIGKILL : return " SIGKILL " ;
# endif
2006-01-10 17:51:06 +00:00
# ifdef SIGUSR1
2007-10-28 19:13:50 +00:00
case SIGUSR1 : return " SIGUSR1 " ;
2006-01-10 17:51:06 +00:00
# endif
2007-10-28 19:13:50 +00:00
case SIGSEGV : return " SIGSEGV " ;
2006-01-10 17:51:06 +00:00
# ifdef SIGUSR2
2007-10-28 19:13:50 +00:00
case SIGUSR2 : return " SIGUSR2 " ;
2006-01-10 17:51:06 +00:00
# endif
# ifdef SIGPIPE
2007-10-28 19:13:50 +00:00
case SIGPIPE : return " SIGPIPE " ;
# endif
# ifdef SIGALRM
case SIGALRM : return " SIGALRM " ;
# endif
case SIGTERM : return " SIGTERM " ;
# ifdef SIGSTKFLT
case SIGSTKFLT : return " SIGSTKFLT " ;
2006-01-10 17:51:06 +00:00
# endif
# ifdef SIGCHLD
2007-10-28 19:13:50 +00:00
case SIGCHLD : return " SIGCHLD " ;
2006-01-10 17:51:06 +00:00
# endif
2007-10-28 19:13:50 +00:00
# ifdef SIGCONT
case SIGCONT : return " SIGCONT " ;
# endif
# ifdef SIGSTOP
case SIGSTOP : return " SIGSTOP " ;
# endif
# ifdef SIGTSTP
case SIGTSTP : return " SIGTSTP " ;
# endif
# ifdef SIGTTIN
case SIGTTIN : return " SIGTTIN " ;
# endif
# ifdef SIGTTOU
case SIGTTOU : return " SIGTTOU " ;
# endif
# ifdef SIGURG
case SIGURG : return " SIGURG " ;
# endif
# ifdef SIGXCPU
case SIGXCPU : return " SIGXCPU " ;
# endif
# ifdef SIGXFSZ
case SIGXFSZ : return " SIGXFSZ " ;
2006-01-10 17:51:06 +00:00
# endif
2007-10-28 19:13:50 +00:00
# ifdef SIGVTALRM
case SIGVTALRM : return " SIGVTALRM " ;
# endif
# ifdef SIGPROF
case SIGPROF : return " SIGPROF " ;
# endif
# ifdef SIGWINCH
case SIGWINCH : return " SIGWINCH " ;
# endif
# ifdef SIGIO
case SIGIO : return " SIGIO " ;
# endif
# ifdef SIGPWR
case SIGPWR : return " SIGPWR " ;
# endif
# ifdef SIGSYS
case SIGSYS : return " SIGSYS " ;
# endif
}
# ifdef SIGRTMIN
if ( sig > = SIGRTMIN & & sig < = SIGRTMAX ) {
pa_xfree ( PA_STATIC_TLS_GET ( signame ) ) ;
t = pa_sprintf_malloc ( " SIGRTMIN+%i " , sig - SIGRTMIN ) ;
PA_STATIC_TLS_SET ( signame , t ) ;
return t ;
2004-09-15 13:03:25 +00:00
}
2007-10-28 19:13:50 +00:00
# endif
# endif
fail :
pa_xfree ( PA_STATIC_TLS_GET ( signame ) ) ;
t = pa_sprintf_malloc ( " SIG%i " , sig ) ;
PA_STATIC_TLS_SET ( signame , t ) ;
return t ;
2004-09-15 13:03:25 +00:00
}
2006-01-10 17:51:06 +00:00
# ifdef HAVE_GRP_H
2004-09-23 22:42:49 +00:00
2004-11-04 21:27:12 +00:00
/* Check whether the specified GID and the group name match */
2004-09-23 22:42:49 +00:00
static int is_group ( gid_t gid , const char * name ) {
2009-08-21 16:02:57 -06:00
struct group * group = NULL ;
2004-09-23 22:42:49 +00:00
int r = - 1 ;
2004-11-01 23:37:36 +00:00
2008-08-09 03:46:46 +02:00
errno = 0 ;
2011-03-02 12:41:26 +01:00
if ( ! ( group = pa_getgrgid_malloc ( gid ) ) ) {
2008-08-09 03:46:46 +02:00
if ( ! errno )
errno = ENOENT ;
2009-08-21 16:02:57 -06:00
pa_log ( " pa_getgrgid_malloc(%u): %s " , gid , pa_cstrerror ( errno ) ) ;
2008-08-09 03:46:46 +02:00
2006-05-22 15:20:46 +00:00
goto finish ;
2004-11-01 23:37:36 +00:00
}
2012-06-06 01:28:14 +05:30
r = pa_streq ( name , group - > gr_name ) ;
2004-11-01 23:37:36 +00:00
finish :
2009-08-21 16:02:57 -06:00
pa_getgrgid_free ( group ) ;
2007-01-04 13:43:45 +00:00
2004-09-23 22:42:49 +00:00
return r ;
}
2004-11-04 21:27:12 +00:00
/* Check the current user is member of the specified group */
2006-02-24 17:14:23 +00:00
int pa_own_uid_in_group ( const char * name , gid_t * gid ) {
2006-01-11 01:17:39 +00:00
GETGROUPS_T * gids , tgid ;
2008-08-19 22:39:54 +02:00
long n = sysconf ( _SC_NGROUPS_MAX ) ;
2008-08-09 03:46:46 +02:00
int r = - 1 , i , k ;
2004-09-23 22:42:49 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( n > 0 ) ;
2007-01-04 13:43:45 +00:00
2008-08-19 22:39:54 +02:00
gids = pa_xmalloc ( sizeof ( GETGROUPS_T ) * ( size_t ) n ) ;
2007-01-04 13:43:45 +00:00
2008-08-19 22:39:54 +02:00
if ( ( n = getgroups ( ( int ) n , gids ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " getgroups(): %s " , pa_cstrerror ( errno ) ) ;
2004-09-23 22:42:49 +00:00
goto finish ;
}
for ( i = 0 ; i < n ; i + + ) {
2008-08-09 03:46:46 +02:00
if ( ( k = is_group ( gids [ i ] , name ) ) < 0 )
goto finish ;
else if ( k > 0 ) {
2004-09-23 22:42:49 +00:00
* gid = gids [ i ] ;
r = 1 ;
goto finish ;
}
}
2008-08-09 03:46:46 +02:00
if ( ( k = is_group ( tgid = getgid ( ) , name ) ) < 0 )
goto finish ;
else if ( k > 0 ) {
2004-09-23 22:42:49 +00:00
* gid = tgid ;
r = 1 ;
goto finish ;
}
r = 0 ;
2007-01-04 13:43:45 +00:00
2004-09-23 22:42:49 +00:00
finish :
pa_xfree ( gids ) ;
return r ;
}
2004-09-27 15:40:18 +00:00
2011-08-24 18:24:46 +02:00
/* Check whether the specific user id is a member of the specified group */
2006-02-24 17:14:23 +00:00
int pa_uid_in_group ( uid_t uid , const char * name ) {
2009-08-21 16:02:57 -06:00
struct group * group = NULL ;
2006-02-24 17:14:23 +00:00
char * * i ;
int r = - 1 ;
2007-01-04 13:43:45 +00:00
2008-08-09 03:46:46 +02:00
errno = 0 ;
2011-03-02 12:41:26 +01:00
if ( ! ( group = pa_getgrnam_malloc ( name ) ) ) {
2008-08-09 03:46:46 +02:00
if ( ! errno )
errno = ENOENT ;
2006-02-24 17:14:23 +00:00
goto finish ;
2008-08-09 03:46:46 +02:00
}
2006-02-24 17:14:23 +00:00
r = 0 ;
2009-08-21 16:02:57 -06:00
for ( i = group - > gr_mem ; * i ; i + + ) {
struct passwd * pw = NULL ;
2007-01-04 13:43:45 +00:00
2009-08-20 00:20:03 +02:00
errno = 0 ;
2009-08-21 16:02:57 -06:00
if ( ! ( pw = pa_getpwnam_malloc ( * i ) ) )
2006-02-24 17:14:23 +00:00
continue ;
2009-08-21 16:02:57 -06:00
if ( pw - > pw_uid = = uid )
2006-02-24 17:14:23 +00:00
r = 1 ;
2009-08-21 16:02:57 -06:00
pa_getpwnam_free ( pw ) ;
if ( r = = 1 )
2006-02-24 17:14:23 +00:00
break ;
}
finish :
2009-08-21 16:02:57 -06:00
pa_getgrnam_free ( group ) ;
2006-02-24 17:14:23 +00:00
return r ;
}
2011-08-24 18:24:46 +02:00
/* Get the GID of a given group, return (gid_t) -1 on failure. */
2006-07-19 17:44:19 +00:00
gid_t pa_get_gid_of_group ( const char * name ) {
gid_t ret = ( gid_t ) - 1 ;
2009-08-21 16:02:57 -06:00
struct group * gr = NULL ;
2006-07-19 17:44:19 +00:00
2008-08-09 03:46:46 +02:00
errno = 0 ;
2011-03-02 12:41:26 +01:00
if ( ! ( gr = pa_getgrnam_malloc ( name ) ) ) {
2008-08-09 03:46:46 +02:00
if ( ! errno )
errno = ENOENT ;
2006-07-19 17:44:19 +00:00
goto finish ;
2008-08-09 03:46:46 +02:00
}
2006-07-19 17:44:19 +00:00
ret = gr - > gr_gid ;
finish :
2009-08-21 16:02:57 -06:00
pa_getgrnam_free ( gr ) ;
2006-07-19 17:44:19 +00:00
return ret ;
}
2006-07-19 21:48:35 +00:00
int pa_check_in_group ( gid_t g ) {
gid_t gids [ NGROUPS_MAX ] ;
int r ;
if ( ( r = getgroups ( NGROUPS_MAX , gids ) ) < 0 )
return - 1 ;
for ( ; r > 0 ; r - - )
if ( gids [ r - 1 ] = = g )
return 1 ;
return 0 ;
}
2006-01-10 17:51:06 +00:00
# else /* HAVE_GRP_H */
2006-02-24 17:14:23 +00:00
int pa_own_uid_in_group ( const char * name , gid_t * gid ) {
2011-01-04 17:12:09 +01:00
errno = ENOTSUP ;
2006-02-24 17:14:23 +00:00
return - 1 ;
2007-01-04 13:43:45 +00:00
2006-02-24 17:14:23 +00:00
}
int pa_uid_in_group ( uid_t uid , const char * name ) {
2011-01-04 17:12:09 +01:00
errno = ENOTSUP ;
2006-01-10 17:51:06 +00:00
return - 1 ;
}
2006-07-19 21:48:35 +00:00
gid_t pa_get_gid_of_group ( const char * name ) {
2011-01-04 17:12:09 +01:00
errno = ENOTSUP ;
2006-07-19 21:48:35 +00:00
return ( gid_t ) - 1 ;
}
int pa_check_in_group ( gid_t g ) {
2011-01-04 17:12:09 +01:00
errno = ENOTSUP ;
2006-07-19 21:48:35 +00:00
return - 1 ;
}
2006-01-10 17:51:06 +00:00
# endif
/* Lock or unlock a file entirely.
( advisory on UNIX , mandatory on Windows ) */
2004-09-28 22:47:48 +00:00
int pa_lock_fd ( int fd , int b ) {
2006-01-10 17:51:06 +00:00
# ifdef F_SETLKW
2009-06-25 12:28:16 +02:00
struct flock f_lock ;
2004-09-27 15:40:18 +00:00
2004-11-20 23:48:18 +00:00
/* Try a R/W lock first */
2007-01-04 13:43:45 +00:00
2009-06-25 12:28:16 +02:00
f_lock . l_type = ( short ) ( b ? F_WRLCK : F_UNLCK ) ;
f_lock . l_whence = SEEK_SET ;
f_lock . l_start = 0 ;
f_lock . l_len = 0 ;
2004-09-27 15:40:18 +00:00
2009-06-25 12:28:16 +02:00
if ( fcntl ( fd , F_SETLKW , & f_lock ) > = 0 )
2004-11-20 23:48:18 +00:00
return 0 ;
2004-09-27 15:40:18 +00:00
2011-08-24 18:24:46 +02:00
/* Perhaps the file descriptor was opened for read only, than try again with a read lock. */
2004-11-20 23:48:18 +00:00
if ( b & & errno = = EBADF ) {
2009-06-25 12:28:16 +02:00
f_lock . l_type = F_RDLCK ;
if ( fcntl ( fd , F_SETLKW , & f_lock ) > = 0 )
2004-11-20 23:48:18 +00:00
return 0 ;
}
2007-01-04 13:43:45 +00:00
2011-02-27 23:09:52 +01:00
pa_log ( " %slock: %s " , ! b ? " un " : " " , pa_cstrerror ( errno ) ) ;
2006-01-10 17:51:06 +00:00
# endif
# ifdef OS_IS_WIN32
2011-02-27 23:09:52 +01:00
HANDLE h = ( HANDLE ) _get_osfhandle ( fd ) ;
2006-01-10 17:51:06 +00:00
if ( b & & LockFile ( h , 0 , 0 , 0xFFFFFFFF , 0xFFFFFFFF ) )
return 0 ;
if ( ! b & & UnlockFile ( h , 0 , 0 , 0xFFFFFFFF , 0xFFFFFFFF ) )
return 0 ;
2006-08-18 21:38:40 +00:00
pa_log ( " %slock failed: 0x%08X " , ! b ? " un " : " " , GetLastError ( ) ) ;
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2006-01-10 17:51:06 +00:00
# endif
2004-11-20 23:48:18 +00:00
return - 1 ;
2004-09-27 15:40:18 +00:00
}
2004-09-27 21:05:55 +00:00
2004-11-04 21:27:12 +00:00
/* Remove trailing newlines from a string */
2004-09-27 21:05:55 +00:00
char * pa_strip_nl ( char * s ) {
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
2004-09-27 21:05:55 +00:00
2010-02-21 21:59:53 +01:00
s [ strcspn ( s , NEWLINE ) ] = 0 ;
return s ;
}
char * pa_strip ( char * s ) {
char * e , * l = NULL ;
/* Drops trailing whitespace. Modifies the string in
* place . Returns pointer to first non - space character */
s + = strspn ( s , WHITESPACE ) ;
for ( e = s ; * e ; e + + )
if ( ! strchr ( WHITESPACE , * e ) )
l = e ;
if ( l )
* ( l + 1 ) = 0 ;
else
* s = 0 ;
2004-09-27 21:05:55 +00:00
return s ;
}
2004-09-28 22:47:48 +00:00
2004-11-04 21:27:12 +00:00
/* Create a temporary lock file and lock it. */
2004-09-28 22:47:48 +00:00
int pa_lock_lockfile ( const char * fn ) {
2009-09-08 23:46:23 +02:00
int fd ;
2007-10-28 19:13:50 +00:00
pa_assert ( fn ) ;
2004-09-28 22:47:48 +00:00
2004-11-21 13:18:56 +00:00
for ( ; ; ) {
struct stat st ;
2007-01-04 13:43:45 +00:00
2009-10-30 03:32:38 +01:00
if ( ( fd = pa_open_cloexec ( fn , O_CREAT | O_RDWR
2007-10-28 19:13:50 +00:00
# ifdef O_NOFOLLOW
| O_NOFOLLOW
# endif
, S_IRUSR | S_IWUSR ) ) < 0 ) {
pa_log_warn ( " Failed to create lock file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
2004-11-21 13:18:56 +00:00
goto fail ;
}
2007-01-04 13:43:45 +00:00
2004-11-21 13:18:56 +00:00
if ( pa_lock_fd ( fd , 1 ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Failed to lock file '%s'. " , fn ) ;
2004-11-21 13:18:56 +00:00
goto fail ;
}
2007-01-04 13:43:45 +00:00
2004-11-21 13:18:56 +00:00
if ( fstat ( fd , & st ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Failed to fstat() file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
2004-11-21 13:18:56 +00:00
goto fail ;
}
2004-09-28 22:47:48 +00:00
2009-04-01 12:35:44 +02:00
/* Check whether the file has been removed meanwhile. When yes,
2007-10-28 19:13:50 +00:00
* restart this loop , otherwise , we ' re done */
2004-11-21 13:18:56 +00:00
if ( st . st_nlink > = 1 )
break ;
2007-01-04 13:43:45 +00:00
2004-11-21 13:18:56 +00:00
if ( pa_lock_fd ( fd , 0 ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Failed to unlock file '%s'. " , fn ) ;
2004-11-21 13:18:56 +00:00
goto fail ;
}
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
if ( pa_close ( fd ) < 0 ) {
pa_log_warn ( " Failed to close file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
fd = - 1 ;
2004-11-21 13:18:56 +00:00
goto fail ;
}
}
2007-01-04 13:43:45 +00:00
2004-09-28 22:47:48 +00:00
return fd ;
fail :
2008-08-09 03:46:46 +02:00
if ( fd > = 0 ) {
int saved_errno = errno ;
2007-10-28 19:13:50 +00:00
pa_close ( fd ) ;
2008-08-09 03:46:46 +02:00
errno = saved_errno ;
}
2004-09-28 22:47:48 +00:00
return - 1 ;
}
2011-08-24 18:24:46 +02:00
/* Unlock a temporary lock file */
2004-11-20 23:48:18 +00:00
int pa_unlock_lockfile ( const char * fn , int fd ) {
2004-09-28 22:47:48 +00:00
int r = 0 ;
2007-10-28 19:13:50 +00:00
pa_assert ( fd > = 0 ) ;
2004-09-28 22:47:48 +00:00
2008-05-21 22:39:40 +00:00
if ( fn ) {
if ( unlink ( fn ) < 0 ) {
pa_log_warn ( " Unable to remove lock file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
r = - 1 ;
}
2004-11-20 23:48:18 +00:00
}
2007-01-04 13:43:45 +00:00
2004-09-28 22:47:48 +00:00
if ( pa_lock_fd ( fd , 0 ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Failed to unlock file '%s'. " , fn ) ;
2004-09-28 22:47:48 +00:00
r = - 1 ;
}
2007-10-28 19:13:50 +00:00
if ( pa_close ( fd ) < 0 ) {
pa_log_warn ( " Failed to close '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
2004-09-28 22:47:48 +00:00
r = - 1 ;
}
return r ;
}
2012-05-15 23:13:28 +02:00
static int check_ours ( const char * p ) {
2008-08-07 02:25:48 +02:00
struct stat st ;
2008-05-15 23:34:41 +00:00
2012-05-15 23:13:28 +02:00
pa_assert ( p ) ;
if ( stat ( p , & st ) < 0 )
return - errno ;
# ifdef HAVE_GETUID
if ( st . st_uid ! = getuid ( ) )
return - EACCES ;
# endif
return 0 ;
}
static char * get_pulse_home ( void ) {
2014-06-08 16:32:59 +03:00
char * h , * ret ;
2012-05-15 23:13:28 +02:00
int t ;
h = pa_get_home_dir_malloc ( ) ;
if ( ! h ) {
2008-08-07 02:25:48 +02:00
pa_log_error ( " Failed to get home directory. " ) ;
return NULL ;
}
2008-05-15 23:34:41 +00:00
2012-05-15 23:13:28 +02:00
t = check_ours ( h ) ;
if ( t < 0 & & t ! = - ENOENT ) {
pa_log_error ( " Home directory not accessible: %s " , pa_cstrerror ( - t ) ) ;
pa_xfree ( h ) ;
return NULL ;
2008-08-07 02:25:48 +02:00
}
2008-05-21 22:39:40 +00:00
2012-05-15 23:13:28 +02:00
/* If the old directory exists, use it. */
2009-08-01 02:03:22 +02:00
ret = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .pulse " , h ) ;
2014-06-08 16:32:59 +03:00
pa_xfree ( h ) ;
if ( access ( ret , F_OK ) > = 0 )
2012-05-15 23:13:28 +02:00
return ret ;
free ( ret ) ;
2009-08-01 02:03:22 +02:00
2012-05-15 23:13:28 +02:00
/* Otherwise go for the XDG compliant directory. */
2014-06-08 16:32:59 +03:00
if ( pa_get_config_home_dir ( & ret ) < 0 )
return NULL ;
2009-08-01 02:03:22 +02:00
return ret ;
2008-08-07 02:25:48 +02:00
}
char * pa_get_state_dir ( void ) {
char * d ;
/* The state directory shall contain dynamic data that should be
* kept across reboots , and is private to this user */
if ( ! ( d = pa_xstrdup ( getenv ( " PULSE_STATE_PATH " ) ) ) )
if ( ! ( d = get_pulse_home ( ) ) )
2008-05-21 22:39:40 +00:00
return NULL ;
2008-06-21 13:55:52 +02:00
2008-08-07 02:25:48 +02:00
/* If PULSE_STATE_PATH and PULSE_RUNTIME_PATH point to the same
* dir then this will break . */
2008-05-15 23:34:41 +00:00
2013-06-27 19:28:09 +02:00
if ( pa_make_secure_dir ( d , 0700U , ( uid_t ) - 1 , ( gid_t ) - 1 , true ) < 0 ) {
2012-07-10 08:32:54 +08:00
pa_log_error ( " Failed to create secure directory (%s): %s " , d , pa_cstrerror ( errno ) ) ;
2008-08-07 02:25:48 +02:00
pa_xfree ( d ) ;
2008-05-15 23:34:41 +00:00
return NULL ;
}
return d ;
}
2009-08-01 02:03:22 +02:00
char * pa_get_home_dir_malloc ( void ) {
char * homedir ;
size_t allocated = 128 ;
for ( ; ; ) {
homedir = pa_xmalloc ( allocated ) ;
if ( ! pa_get_home_dir ( homedir , allocated ) ) {
pa_xfree ( homedir ) ;
return NULL ;
}
if ( strlen ( homedir ) < allocated - 1 )
break ;
pa_xfree ( homedir ) ;
allocated * = 2 ;
}
return homedir ;
}
2014-06-08 16:32:57 +03:00
int pa_append_to_home_dir ( const char * path , char * * _r ) {
char * home_dir ;
pa_assert ( path ) ;
pa_assert ( _r ) ;
home_dir = pa_get_home_dir_malloc ( ) ;
if ( ! home_dir ) {
pa_log ( " Failed to get home directory. " ) ;
return - PA_ERR_NOENTITY ;
}
* _r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , home_dir , path ) ;
pa_xfree ( home_dir ) ;
return 0 ;
}
2014-06-08 16:32:59 +03:00
int pa_get_config_home_dir ( char * * _r ) {
const char * e ;
char * home_dir ;
pa_assert ( _r ) ;
e = getenv ( " XDG_CONFIG_HOME " ) ;
if ( e & & * e ) {
* _r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " pulse " , e ) ;
return 0 ;
}
home_dir = pa_get_home_dir_malloc ( ) ;
if ( ! home_dir )
return - PA_ERR_NOENTITY ;
* _r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .config " PA_PATH_SEP " pulse " , home_dir ) ;
pa_xfree ( home_dir ) ;
return 0 ;
}
2014-06-08 16:33:00 +03:00
int pa_append_to_config_home_dir ( const char * path , char * * _r ) {
int r ;
char * config_home_dir ;
pa_assert ( path ) ;
pa_assert ( _r ) ;
r = pa_get_config_home_dir ( & config_home_dir ) ;
if ( r < 0 )
return r ;
* _r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , config_home_dir , path ) ;
pa_xfree ( config_home_dir ) ;
return 0 ;
}
2009-08-01 02:03:22 +02:00
char * pa_get_binary_name_malloc ( void ) {
char * t ;
size_t allocated = 128 ;
for ( ; ; ) {
t = pa_xmalloc ( allocated ) ;
if ( ! pa_get_binary_name ( t , allocated ) ) {
pa_xfree ( t ) ;
return NULL ;
}
if ( strlen ( t ) < allocated - 1 )
break ;
pa_xfree ( t ) ;
allocated * = 2 ;
}
return t ;
}
2008-08-07 02:25:48 +02:00
static char * make_random_dir ( mode_t m ) {
static const char table [ ] =
" abcdefghijklmnopqrstuvwxyz "
" ABCDEFGHIJKLMNOPQRSTUVWXYZ "
" 0123456789 " ;
2008-09-23 00:26:31 +02:00
char * fn ;
size_t pathlen ;
2008-08-07 02:25:48 +02:00
2009-09-17 21:06:54 +02:00
fn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " pulse-XXXXXXXXXXXX " , pa_get_temp_dir ( ) ) ;
2008-09-23 00:26:31 +02:00
pathlen = strlen ( fn ) ;
2008-08-07 02:25:48 +02:00
for ( ; ; ) {
2008-09-23 00:26:31 +02:00
size_t i ;
2008-08-07 02:25:48 +02:00
int r ;
mode_t u ;
int saved_errno ;
2008-09-23 00:26:31 +02:00
for ( i = pathlen - 12 ; i < pathlen ; i + + )
2008-08-07 02:25:48 +02:00
fn [ i ] = table [ rand ( ) % ( sizeof ( table ) - 1 ) ] ;
u = umask ( ( ~ m ) & 0777 ) ;
2011-01-06 00:51:33 +01:00
# ifndef OS_IS_WIN32
2008-08-07 02:25:48 +02:00
r = mkdir ( fn , m ) ;
2011-01-06 00:51:33 +01:00
# else
r = mkdir ( fn ) ;
# endif
2008-09-23 00:26:31 +02:00
2008-08-07 02:25:48 +02:00
saved_errno = errno ;
umask ( u ) ;
2008-09-23 00:26:31 +02:00
errno = saved_errno ;
2008-08-07 02:25:48 +02:00
if ( r > = 0 )
2008-09-23 00:26:31 +02:00
return fn ;
2008-08-07 02:25:48 +02:00
if ( errno ! = EEXIST ) {
pa_log_error ( " Failed to create random directory %s: %s " , fn , pa_cstrerror ( errno ) ) ;
2008-09-23 00:26:31 +02:00
pa_xfree ( fn ) ;
2008-08-07 02:25:48 +02:00
return NULL ;
}
}
2008-05-21 22:39:40 +00:00
}
2008-08-07 02:25:48 +02:00
static int make_random_dir_and_link ( mode_t m , const char * k ) {
char * p ;
if ( ! ( p = make_random_dir ( m ) ) )
return - 1 ;
2011-01-06 00:51:33 +01:00
# ifdef HAVE_SYMLINK
2008-08-07 02:25:48 +02:00
if ( symlink ( p , k ) < 0 ) {
int saved_errno = errno ;
if ( errno ! = EEXIST )
pa_log_error ( " Failed to symlink %s to %s: %s " , k , p , pa_cstrerror ( errno ) ) ;
rmdir ( p ) ;
pa_xfree ( p ) ;
errno = saved_errno ;
return - 1 ;
}
2011-01-06 02:10:45 +01:00
# else
pa_xfree ( p ) ;
return - 1 ;
2011-01-06 00:51:33 +01:00
# endif
2008-08-07 02:25:48 +02:00
2009-02-18 21:23:41 +02:00
pa_xfree ( p ) ;
2008-08-07 02:25:48 +02:00
return 0 ;
}
char * pa_get_runtime_dir ( void ) {
char * d , * k = NULL , * p = NULL , * t = NULL , * mid ;
2008-09-12 17:39:08 +03:00
mode_t m ;
2008-08-07 02:25:48 +02:00
/* The runtime directory shall contain dynamic data that needs NOT
2011-08-24 18:24:46 +02:00
* to be kept across reboots and is usually private to the user ,
2008-08-07 02:25:48 +02:00
* except in system mode , where it might be accessible by other
* users , too . Since we need POSIX locking and UNIX sockets in
2012-05-15 19:06:53 +02:00
* this directory , we try XDG_RUNTIME_DIR first , and if that isn ' t
* set create a directory in $ HOME and link it to a random subdir
* in / tmp , if it was not explicitly configured . */
2008-08-07 02:25:48 +02:00
2008-09-12 17:39:08 +03:00
m = pa_in_system_mode ( ) ? 0755U : 0700U ;
2008-08-07 02:25:48 +02:00
2012-05-15 19:06:53 +02:00
/* Use the explicitly configured value if it is set */
d = getenv ( " PULSE_RUNTIME_PATH " ) ;
if ( d ) {
2008-08-07 02:25:48 +02:00
2013-06-27 19:28:09 +02:00
if ( pa_make_secure_dir ( d , m , ( uid_t ) - 1 , ( gid_t ) - 1 , true ) < 0 ) {
2012-07-10 08:32:54 +08:00
pa_log_error ( " Failed to create secure directory (%s): %s " , d , pa_cstrerror ( errno ) ) ;
2008-08-07 02:25:48 +02:00
goto fail ;
}
return pa_xstrdup ( d ) ;
}
2012-05-15 19:06:53 +02:00
/* Use the XDG standard for the runtime directory. */
d = getenv ( " XDG_RUNTIME_DIR " ) ;
if ( d ) {
2014-12-03 12:46:28 +01:00
# ifdef HAVE_GETUID
2013-11-12 07:52:48 +01:00
struct stat st ;
if ( stat ( d , & st ) = = 0 & & st . st_uid ! = getuid ( ) ) {
pa_log ( _ ( " XDG_RUNTIME_DIR (%s) is not owned by us (uid %d), but by uid %d! "
" (This could e g happen if you try to connect to a non-root PulseAudio as a root user, over the native protocol. Don't do that.) " ) ,
d , getuid ( ) , st . st_uid ) ;
goto fail ;
}
2014-12-03 12:46:28 +01:00
# endif
2013-11-12 07:52:48 +01:00
2012-05-15 19:06:53 +02:00
k = pa_sprintf_malloc ( " %s " PA_PATH_SEP " pulse " , d ) ;
2013-06-27 19:28:09 +02:00
if ( pa_make_secure_dir ( k , m , ( uid_t ) - 1 , ( gid_t ) - 1 , true ) < 0 ) {
2012-07-10 08:32:54 +08:00
pa_log_error ( " Failed to create secure directory (%s): %s " , k , pa_cstrerror ( errno ) ) ;
2012-05-15 19:06:53 +02:00
goto fail ;
}
return k ;
}
/* XDG_RUNTIME_DIR wasn't set, use the old legacy fallback */
d = get_pulse_home ( ) ;
if ( ! d )
2008-08-07 02:25:48 +02:00
goto fail ;
2013-06-27 19:28:09 +02:00
if ( pa_make_secure_dir ( d , m , ( uid_t ) - 1 , ( gid_t ) - 1 , true ) < 0 ) {
2012-07-10 08:32:54 +08:00
pa_log_error ( " Failed to create secure directory (%s): %s " , d , pa_cstrerror ( errno ) ) ;
2009-02-18 21:21:25 +02:00
pa_xfree ( d ) ;
2008-09-12 17:39:08 +03:00
goto fail ;
}
2012-05-15 19:06:53 +02:00
mid = pa_machine_id ( ) ;
if ( ! mid ) {
2008-08-07 02:25:48 +02:00
pa_xfree ( d ) ;
goto fail ;
}
2009-07-23 19:12:53 +02:00
k = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s-runtime " , d , mid ) ;
2008-08-07 02:25:48 +02:00
pa_xfree ( d ) ;
pa_xfree ( mid ) ;
for ( ; ; ) {
2011-08-24 18:24:46 +02:00
/* OK, first let's check if the "runtime" symlink already exists */
2008-08-07 02:25:48 +02:00
2012-05-15 19:06:53 +02:00
p = pa_readlink ( k ) ;
if ( ! p ) {
2008-08-07 02:25:48 +02:00
if ( errno ! = ENOENT ) {
pa_log_error ( " Failed to stat runtime directory %s: %s " , k , pa_cstrerror ( errno ) ) ;
goto fail ;
}
2011-01-06 02:10:45 +01:00
# ifdef HAVE_SYMLINK
2008-08-07 02:25:48 +02:00
/* Hmm, so the runtime directory didn't exist yet, so let's
* create one in / tmp and symlink that to it */
if ( make_random_dir_and_link ( 0700 , k ) < 0 ) {
/* Mhmm, maybe another process was quicker than us,
* let ' s check if that was valid */
if ( errno = = EEXIST )
continue ;
goto fail ;
}
2011-01-06 02:10:45 +01:00
# else
2012-10-30 11:35:26 +01:00
/* No symlink possible, so let's just create the runtime directly
* Do not check again if it exists since it cannot be a symlink */
if ( mkdir ( k ) < 0 & & errno ! = EEXIST )
2011-01-06 02:10:45 +01:00
goto fail ;
# endif
2008-08-07 02:25:48 +02:00
return k ;
}
/* Make sure that this actually makes sense */
if ( ! pa_is_path_absolute ( p ) ) {
pa_log_error ( " Path %s in link %s is not absolute. " , p , k ) ;
2008-08-09 03:46:46 +02:00
errno = ENOENT ;
2008-08-07 02:25:48 +02:00
goto fail ;
}
2011-06-23 22:21:03 +02:00
/* Hmm, so this symlink is still around, make sure nobody fools us */
2011-01-06 00:51:33 +01:00
# ifdef HAVE_LSTAT
2011-06-23 22:21:03 +02:00
{
struct stat st ;
2008-08-07 02:25:48 +02:00
if ( lstat ( p , & st ) < 0 ) {
if ( errno ! = ENOENT ) {
pa_log_error ( " Failed to stat runtime directory %s: %s " , p , pa_cstrerror ( errno ) ) ;
goto fail ;
}
} else {
if ( S_ISDIR ( st . st_mode ) & &
( st . st_uid = = getuid ( ) ) & &
( ( st . st_mode & 0777 ) = = 0700 ) ) {
pa_xfree ( p ) ;
return k ;
}
pa_log_info ( " Hmm, runtime path exists, but points to an invalid directory. Changing runtime directory. " ) ;
}
2011-06-23 22:21:03 +02:00
}
2011-01-06 00:51:33 +01:00
# endif
2008-08-07 02:25:48 +02:00
pa_xfree ( p ) ;
p = NULL ;
/* Hmm, so the link points to some nonexisting or invalid
* dir . Let ' s replace it by a new link . We first create a
* temporary link and then rename that to allow concurrent
* execution of this function . */
t = pa_sprintf_malloc ( " %s.tmp " , k ) ;
if ( make_random_dir_and_link ( 0700 , t ) < 0 ) {
if ( errno ! = EEXIST ) {
pa_log_error ( " Failed to symlink %s: %s " , t , pa_cstrerror ( errno ) ) ;
goto fail ;
}
pa_xfree ( t ) ;
t = NULL ;
2011-08-24 18:24:46 +02:00
/* Hmm, someone else was quicker then us. Let's give
2008-08-07 02:25:48 +02:00
* him some time to finish , and retry . */
pa_msleep ( 10 ) ;
continue ;
}
/* OK, we succeeded in creating the temporary symlink, so
* let ' s rename it */
if ( rename ( t , k ) < 0 ) {
pa_log_error ( " Failed to rename %s to %s: %s " , t , k , pa_cstrerror ( errno ) ) ;
goto fail ;
}
pa_xfree ( t ) ;
return k ;
}
fail :
pa_xfree ( p ) ;
pa_xfree ( k ) ;
pa_xfree ( t ) ;
return NULL ;
2008-05-21 22:39:40 +00:00
}
2004-11-04 21:27:12 +00:00
/* Try to open a configuration file. If "env" is specified, open the
* value of the specified environment variable . Otherwise look for a
* file " local " in the home directory or a file " global " in global
* file system . If " result " is non - NULL , a pointer to a newly
* allocated buffer containing the used configuration file is
* stored there . */
2008-05-15 23:34:41 +00:00
FILE * pa_open_config_file ( const char * global , const char * local , const char * env , char * * result ) {
2006-01-10 17:51:06 +00:00
const char * fn ;
2011-02-27 23:09:52 +01:00
FILE * f ;
2006-01-10 17:51:06 +00:00
if ( env & & ( fn = getenv ( env ) ) ) {
2009-10-30 04:54:19 +01:00
if ( ( f = pa_fopen_cloexec ( fn , " r " ) ) ) {
2008-05-15 23:34:41 +00:00
if ( result )
* result = pa_xstrdup ( fn ) ;
2006-01-10 17:51:06 +00:00
2008-05-15 23:34:41 +00:00
return f ;
}
pa_log_warn ( " Failed to open configuration file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
return NULL ;
2004-11-04 21:27:12 +00:00
}
2006-07-19 21:48:35 +00:00
if ( local ) {
const char * e ;
2008-05-15 23:34:41 +00:00
char * lfn ;
2009-08-01 02:03:22 +02:00
char * h ;
2006-01-10 17:51:06 +00:00
2012-05-15 23:25:00 +02:00
if ( ( e = getenv ( " PULSE_CONFIG_PATH " ) ) ) {
2008-05-15 23:34:41 +00:00
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , e , local ) ;
2012-05-15 23:25:00 +02:00
f = pa_fopen_cloexec ( fn , " r " ) ;
} else if ( ( h = pa_get_home_dir_malloc ( ) ) ) {
2008-05-15 23:34:41 +00:00
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .pulse " PA_PATH_SEP " %s " , h , local ) ;
2012-05-15 23:25:00 +02:00
f = pa_fopen_cloexec ( fn , " r " ) ;
if ( ! f ) {
free ( lfn ) ;
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .config/pulse " PA_PATH_SEP " %s " , h , local ) ;
f = pa_fopen_cloexec ( fn , " r " ) ;
}
2009-08-01 02:03:22 +02:00
pa_xfree ( h ) ;
} else
2008-08-06 03:04:22 +02:00
return NULL ;
2008-05-15 23:34:41 +00:00
2012-05-15 23:25:00 +02:00
if ( f ) {
2008-05-15 23:34:41 +00:00
if ( result )
* result = pa_xstrdup ( fn ) ;
2007-11-24 16:22:23 +00:00
2008-05-15 23:34:41 +00:00
pa_xfree ( lfn ) ;
return f ;
}
if ( errno ! = ENOENT ) {
pa_log_warn ( " Failed to open configuration file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
pa_xfree ( lfn ) ;
return NULL ;
2007-11-24 16:22:23 +00:00
}
2006-07-19 21:48:35 +00:00
2008-05-15 23:34:41 +00:00
pa_xfree ( lfn ) ;
}
if ( global ) {
2011-02-27 23:09:52 +01:00
char * gfn ;
2007-01-04 13:43:45 +00:00
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2011-02-27 23:09:52 +01:00
if ( strncmp ( global , PA_DEFAULT_CONFIG_DIR , strlen ( PA_DEFAULT_CONFIG_DIR ) ) = = 0 )
gfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " etc " PA_PATH_SEP " pulse%s " ,
pa_win32_get_toplevel ( NULL ) ,
global + strlen ( PA_DEFAULT_CONFIG_DIR ) ) ;
else
2006-01-10 17:51:06 +00:00
# endif
2011-02-27 23:09:52 +01:00
gfn = pa_xstrdup ( global ) ;
2007-01-04 13:43:45 +00:00
2011-02-27 23:09:52 +01:00
if ( ( f = pa_fopen_cloexec ( gfn , " r " ) ) ) {
2008-05-15 23:34:41 +00:00
if ( result )
2011-02-27 23:09:52 +01:00
* result = gfn ;
else
pa_xfree ( gfn ) ;
2007-02-05 10:26:14 +00:00
2008-05-15 23:34:41 +00:00
return f ;
2004-11-04 21:27:12 +00:00
}
2011-02-27 23:09:52 +01:00
pa_xfree ( gfn ) ;
2008-08-09 03:46:46 +02:00
}
2008-05-15 23:34:41 +00:00
2008-08-09 03:46:46 +02:00
errno = ENOENT ;
2008-05-15 23:34:41 +00:00
return NULL ;
}
char * pa_find_config_file ( const char * global , const char * local , const char * env ) {
const char * fn ;
if ( env & & ( fn = getenv ( env ) ) ) {
if ( access ( fn , R_OK ) = = 0 )
return pa_xstrdup ( fn ) ;
pa_log_warn ( " Failed to access configuration file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
2004-11-04 21:27:12 +00:00
return NULL ;
}
2008-05-15 23:34:41 +00:00
if ( local ) {
const char * e ;
char * lfn ;
2009-08-01 02:03:22 +02:00
char * h ;
2008-05-15 23:34:41 +00:00
if ( ( e = getenv ( " PULSE_CONFIG_PATH " ) ) )
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , e , local ) ;
2009-08-01 02:03:22 +02:00
else if ( ( h = pa_get_home_dir_malloc ( ) ) ) {
2008-05-15 23:34:41 +00:00
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .pulse " PA_PATH_SEP " %s " , h , local ) ;
2009-08-01 02:03:22 +02:00
pa_xfree ( h ) ;
} else
2008-08-06 03:04:22 +02:00
return NULL ;
2008-05-15 23:34:41 +00:00
if ( access ( fn , R_OK ) = = 0 ) {
char * r = pa_xstrdup ( fn ) ;
pa_xfree ( lfn ) ;
return r ;
}
if ( errno ! = ENOENT ) {
pa_log_warn ( " Failed to access configuration file '%s': %s " , fn , pa_cstrerror ( errno ) ) ;
pa_xfree ( lfn ) ;
return NULL ;
}
pa_xfree ( lfn ) ;
}
if ( global ) {
2011-02-27 23:09:52 +01:00
char * gfn ;
2008-05-15 23:34:41 +00:00
# ifdef OS_IS_WIN32
2011-02-27 23:09:52 +01:00
if ( strncmp ( global , PA_DEFAULT_CONFIG_DIR , strlen ( PA_DEFAULT_CONFIG_DIR ) ) = = 0 )
gfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " etc " PA_PATH_SEP " pulse%s " ,
pa_win32_get_toplevel ( NULL ) ,
global + strlen ( PA_DEFAULT_CONFIG_DIR ) ) ;
else
2006-01-10 17:51:06 +00:00
# endif
2011-02-27 23:09:52 +01:00
gfn = pa_xstrdup ( global ) ;
2006-01-10 17:51:06 +00:00
2011-02-27 23:09:52 +01:00
if ( access ( gfn , R_OK ) = = 0 )
return gfn ;
pa_xfree ( gfn ) ;
2008-08-09 03:46:46 +02:00
}
errno = ENOENT ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
return NULL ;
2004-11-04 21:27:12 +00:00
}
2007-01-04 13:43:45 +00:00
2004-11-07 20:48:46 +00:00
/* Format the specified data as a hexademical string */
char * pa_hexstr ( const uint8_t * d , size_t dlength , char * s , size_t slength ) {
size_t i = 0 , j = 0 ;
const char hex [ ] = " 0123456789abcdef " ;
2007-10-28 19:13:50 +00:00
pa_assert ( d ) ;
pa_assert ( s ) ;
pa_assert ( slength > 0 ) ;
2004-11-07 20:48:46 +00:00
2011-03-19 13:59:16 +01:00
while ( j + 2 < slength & & i < dlength ) {
2004-11-07 20:48:46 +00:00
s [ j + + ] = hex [ * d > > 4 ] ;
s [ j + + ] = hex [ * d & 0xF ] ;
d + + ;
i + + ;
}
s [ j < slength ? j : slength ] = 0 ;
return s ;
}
2004-11-08 23:48:19 +00:00
/* Convert a hexadecimal digit to a number or -1 if invalid */
static int hexc ( char c ) {
if ( c > = ' 0 ' & & c < = ' 9 ' )
return c - ' 0 ' ;
if ( c > = ' A ' & & c < = ' F ' )
return c - ' A ' + 10 ;
if ( c > = ' a ' & & c < = ' f ' )
return c - ' a ' + 10 ;
2008-08-09 03:46:46 +02:00
errno = EINVAL ;
2004-11-08 23:48:19 +00:00
return - 1 ;
}
/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
size_t pa_parsehex ( const char * p , uint8_t * d , size_t dlength ) {
size_t j = 0 ;
2007-10-28 19:13:50 +00:00
pa_assert ( p ) ;
pa_assert ( d ) ;
2004-11-08 23:48:19 +00:00
while ( j < dlength & & * p ) {
int b ;
if ( ( b = hexc ( * ( p + + ) ) ) < 0 )
return ( size_t ) - 1 ;
2007-01-04 13:43:45 +00:00
2004-11-08 23:48:19 +00:00
d [ j ] = ( uint8_t ) ( b < < 4 ) ;
if ( ! * p )
return ( size_t ) - 1 ;
if ( ( b = hexc ( * ( p + + ) ) ) < 0 )
return ( size_t ) - 1 ;
d [ j ] | = ( uint8_t ) b ;
j + + ;
}
return j ;
}
2004-11-09 00:14:07 +00:00
2004-11-11 21:18:33 +00:00
/* Returns nonzero when *s starts with *pfx */
2013-06-27 19:28:09 +02:00
bool pa_startswith ( const char * s , const char * pfx ) {
2004-11-11 21:18:33 +00:00
size_t l ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
pa_assert ( pfx ) ;
2007-01-04 13:43:45 +00:00
2004-11-11 21:18:33 +00:00
l = strlen ( pfx ) ;
return strlen ( s ) > = l & & strncmp ( s , pfx , l ) = = 0 ;
}
2006-02-21 23:34:50 +00:00
/* Returns nonzero when *s ends with *sfx */
2013-06-27 19:28:09 +02:00
bool pa_endswith ( const char * s , const char * sfx ) {
2006-02-21 23:34:50 +00:00
size_t l1 , l2 ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
pa_assert ( sfx ) ;
2007-01-04 13:43:45 +00:00
2006-02-21 23:34:50 +00:00
l1 = strlen ( s ) ;
l2 = strlen ( sfx ) ;
2012-06-06 01:28:14 +05:30
return l1 > = l2 & & pa_streq ( s + l1 - l2 , sfx ) ;
2006-02-21 23:34:50 +00:00
}
2013-06-27 19:28:09 +02:00
bool pa_is_path_absolute ( const char * fn ) {
2008-05-15 23:34:41 +00:00
pa_assert ( fn ) ;
2004-11-11 21:18:33 +00:00
2006-01-10 17:51:06 +00:00
# ifndef OS_IS_WIN32
2008-05-15 23:34:41 +00:00
return * fn = = ' / ' ;
2006-01-10 17:51:06 +00:00
# else
2008-05-15 23:34:41 +00:00
return strlen ( fn ) > = 3 & & isalpha ( fn [ 0 ] ) & & fn [ 1 ] = = ' : ' & & fn [ 2 ] = = ' \\ ' ;
2006-01-10 17:51:06 +00:00
# endif
2008-05-15 23:34:41 +00:00
}
2006-01-10 17:51:06 +00:00
2008-05-15 23:34:41 +00:00
char * pa_make_path_absolute ( const char * p ) {
char * r ;
char * cwd ;
2006-07-19 17:44:19 +00:00
2008-05-15 23:34:41 +00:00
pa_assert ( p ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
if ( pa_is_path_absolute ( p ) )
return pa_xstrdup ( p ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
if ( ! ( cwd = pa_getcwd ( ) ) )
return pa_xstrdup ( p ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , cwd , p ) ;
pa_xfree ( cwd ) ;
return r ;
}
2006-01-10 17:51:06 +00:00
2012-12-13 08:26:05 +02:00
/* If fn is NULL, return the PulseAudio runtime or state dir (depending on the
* rt parameter ) . If fn is non - NULL and starts with / , return fn . Otherwise ,
* append fn to the runtime / state dir and return it . */
2013-06-27 19:28:09 +02:00
static char * get_path ( const char * fn , bool prependmid , bool rt ) {
2008-05-15 23:34:41 +00:00
char * rtp ;
2006-01-10 17:51:06 +00:00
2008-05-21 22:39:40 +00:00
rtp = rt ? pa_get_runtime_dir ( ) : pa_get_state_dir ( ) ;
2008-05-15 23:34:41 +00:00
if ( fn ) {
2012-03-14 01:41:48 +00:00
char * r , * canonical_rtp ;
2008-08-07 02:28:47 +02:00
2011-12-19 12:28:41 +01:00
if ( pa_is_path_absolute ( fn ) ) {
pa_xfree ( rtp ) ;
2009-08-28 15:16:53 +03:00
return pa_xstrdup ( fn ) ;
2011-12-19 12:28:41 +01:00
}
2009-08-28 15:16:53 +03:00
if ( ! rtp )
return NULL ;
2012-03-14 01:41:48 +00:00
/* Hopefully make the path smaller to avoid 108 char limit (fdo#44680) */
if ( ( canonical_rtp = pa_realpath ( rtp ) ) ) {
if ( strlen ( rtp ) > = strlen ( canonical_rtp ) )
pa_xfree ( rtp ) ;
else {
pa_xfree ( canonical_rtp ) ;
canonical_rtp = rtp ;
}
} else
canonical_rtp = rtp ;
2008-08-07 02:28:47 +02:00
if ( prependmid ) {
char * mid ;
if ( ! ( mid = pa_machine_id ( ) ) ) {
2012-03-14 01:41:48 +00:00
pa_xfree ( canonical_rtp ) ;
2008-08-07 02:28:47 +02:00
return NULL ;
}
2012-03-14 01:41:48 +00:00
r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s-%s " , canonical_rtp , mid , fn ) ;
2008-08-07 02:28:47 +02:00
pa_xfree ( mid ) ;
} else
2012-03-14 01:41:48 +00:00
r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , canonical_rtp , fn ) ;
2008-08-07 02:28:47 +02:00
2012-03-14 01:41:48 +00:00
pa_xfree ( canonical_rtp ) ;
2008-05-15 23:34:41 +00:00
return r ;
} else
return rtp ;
2004-11-11 21:18:33 +00:00
}
2004-11-21 18:15:33 +00:00
2008-05-21 22:39:40 +00:00
char * pa_runtime_path ( const char * fn ) {
2013-06-27 19:28:09 +02:00
return get_path ( fn , false , true ) ;
2008-05-21 22:39:40 +00:00
}
2013-06-27 19:28:09 +02:00
char * pa_state_path ( const char * fn , bool appendmid ) {
return get_path ( fn , appendmid , false ) ;
2008-05-21 22:39:40 +00:00
}
2004-12-11 00:10:41 +00:00
/* Convert the string s to a signed integer in *ret_i */
int pa_atoi ( const char * s , int32_t * ret_i ) {
long l ;
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
pa_assert ( ret_i ) ;
2011-03-27 21:35:03 +03:00
if ( pa_atol ( s , & l ) < 0 )
2007-10-28 19:13:50 +00:00
return - 1 ;
2008-08-09 03:46:46 +02:00
if ( ( int32_t ) l ! = l ) {
errno = ERANGE ;
2004-12-11 00:10:41 +00:00
return - 1 ;
2008-08-09 03:46:46 +02:00
}
2004-12-11 00:10:41 +00:00
* ret_i = ( int32_t ) l ;
2007-01-04 13:43:45 +00:00
2004-12-11 00:10:41 +00:00
return 0 ;
}
/* Convert the string s to an unsigned integer in *ret_u */
int pa_atou ( const char * s , uint32_t * ret_u ) {
char * x = NULL ;
unsigned long l ;
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
pa_assert ( ret_u ) ;
2015-02-25 11:56:47 +02:00
/* strtoul() ignores leading spaces. We don't. */
2015-11-20 04:20:36 +01:00
if ( isspace ( ( unsigned char ) * s ) ) {
2015-02-25 11:56:47 +02:00
errno = EINVAL ;
return - 1 ;
}
/* strtoul() accepts strings that start with a minus sign. In that case the
* original negative number gets negated , and strtoul ( ) returns the negated
* result . We don ' t want that kind of behaviour . strtoul ( ) also allows a
* leading plus sign , which is also a thing that we don ' t want . */
if ( * s = = ' - ' | | * s = = ' + ' ) {
errno = EINVAL ;
return - 1 ;
}
2007-10-28 19:13:50 +00:00
errno = 0 ;
2004-12-11 00:10:41 +00:00
l = strtoul ( s , & x , 0 ) ;
2015-02-25 11:56:47 +02:00
/* If x doesn't point to the end of s, there was some trailing garbage in
* the string . If x points to s , no conversion was done ( empty string ) . */
if ( ! x | | * x | | x = = s | | errno ) {
2008-08-09 03:46:46 +02:00
if ( ! errno )
errno = EINVAL ;
2007-10-28 19:13:50 +00:00
return - 1 ;
2008-08-09 03:46:46 +02:00
}
2007-10-28 19:13:50 +00:00
2008-08-09 03:46:46 +02:00
if ( ( uint32_t ) l ! = l ) {
errno = ERANGE ;
2004-12-11 00:10:41 +00:00
return - 1 ;
2008-08-09 03:46:46 +02:00
}
2004-12-11 00:10:41 +00:00
* ret_u = ( uint32_t ) l ;
2007-01-04 13:43:45 +00:00
2004-12-11 00:10:41 +00:00
return 0 ;
}
2007-10-28 19:13:50 +00:00
2011-03-27 21:35:03 +03:00
/* Convert the string s to a signed long integer in *ret_l. */
int pa_atol ( const char * s , long * ret_l ) {
char * x = NULL ;
long l ;
pa_assert ( s ) ;
pa_assert ( ret_l ) ;
2015-02-25 11:56:47 +02:00
/* strtol() ignores leading spaces. We don't. */
2015-11-20 04:20:36 +01:00
if ( isspace ( ( unsigned char ) * s ) ) {
2015-02-25 11:56:47 +02:00
errno = EINVAL ;
return - 1 ;
}
/* strtol() accepts leading plus signs, but that's ugly, so we don't allow
* that . */
if ( * s = = ' + ' ) {
errno = EINVAL ;
return - 1 ;
}
2011-03-27 21:35:03 +03:00
errno = 0 ;
l = strtol ( s , & x , 0 ) ;
2015-02-25 11:56:47 +02:00
/* If x doesn't point to the end of s, there was some trailing garbage in
* the string . If x points to s , no conversion was done ( at least an empty
* string can trigger this ) . */
if ( ! x | | * x | | x = = s | | errno ) {
2011-03-27 21:35:03 +03:00
if ( ! errno )
errno = EINVAL ;
return - 1 ;
}
* ret_l = l ;
return 0 ;
}
2015-05-20 22:35:27 +03:00
# ifdef HAVE_STRTOD_L
2007-10-28 19:13:50 +00:00
static locale_t c_locale = NULL ;
static void c_locale_destroy ( void ) {
freelocale ( c_locale ) ;
}
# endif
2008-06-11 00:37:41 +00:00
int pa_atod ( const char * s , double * ret_d ) {
2007-10-28 19:13:50 +00:00
char * x = NULL ;
2008-06-11 00:37:41 +00:00
double f ;
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
2008-06-11 00:37:41 +00:00
pa_assert ( ret_d ) ;
2007-10-28 19:13:50 +00:00
2015-02-25 11:56:47 +02:00
/* strtod() ignores leading spaces. We don't. */
2015-11-20 04:20:36 +01:00
if ( isspace ( ( unsigned char ) * s ) ) {
2015-02-25 11:56:47 +02:00
errno = EINVAL ;
return - 1 ;
}
/* strtod() accepts leading plus signs, but that's ugly, so we don't allow
* that . */
if ( * s = = ' + ' ) {
errno = EINVAL ;
return - 1 ;
}
2007-10-28 19:13:50 +00:00
/* This should be locale independent */
2015-05-20 22:35:27 +03:00
# ifdef HAVE_STRTOD_L
2007-10-28 19:13:50 +00:00
PA_ONCE_BEGIN {
if ( ( c_locale = newlocale ( LC_ALL_MASK , " C " , NULL ) ) )
atexit ( c_locale_destroy ) ;
} PA_ONCE_END ;
if ( c_locale ) {
errno = 0 ;
2008-06-11 00:37:41 +00:00
f = strtod_l ( s , & x , c_locale ) ;
2007-10-28 19:13:50 +00:00
} else
# endif
{
errno = 0 ;
f = strtod ( s , & x ) ;
}
2015-02-25 11:56:47 +02:00
/* If x doesn't point to the end of s, there was some trailing garbage in
* the string . If x points to s , no conversion was done ( at least an empty
* string can trigger this ) . */
if ( ! x | | * x | | x = = s | | errno ) {
2008-08-09 03:46:46 +02:00
if ( ! errno )
errno = EINVAL ;
return - 1 ;
}
2007-10-28 19:13:50 +00:00
2015-04-10 12:44:02 +03:00
if ( isnan ( f ) ) {
errno = EINVAL ;
return - 1 ;
}
2008-08-09 03:46:46 +02:00
* ret_d = f ;
return 0 ;
2007-10-28 19:13:50 +00:00
}
/* Same as snprintf, but guarantees NUL-termination on every platform */
2008-08-19 22:39:54 +02:00
size_t pa_snprintf ( char * str , size_t size , const char * format , . . . ) {
size_t ret ;
2007-10-28 19:13:50 +00:00
va_list ap ;
pa_assert ( str ) ;
pa_assert ( size > 0 ) ;
pa_assert ( format ) ;
va_start ( ap , format ) ;
2008-05-15 23:34:41 +00:00
ret = pa_vsnprintf ( str , size , format , ap ) ;
2007-10-28 19:13:50 +00:00
va_end ( ap ) ;
2008-05-15 23:34:41 +00:00
return ret ;
}
/* Same as vsnprintf, but guarantees NUL-termination on every platform */
2008-08-19 22:39:54 +02:00
size_t pa_vsnprintf ( char * str , size_t size , const char * format , va_list ap ) {
2008-05-15 23:34:41 +00:00
int ret ;
pa_assert ( str ) ;
pa_assert ( size > 0 ) ;
pa_assert ( format ) ;
ret = vsnprintf ( str , size , format , ap ) ;
2007-10-28 19:13:50 +00:00
str [ size - 1 ] = 0 ;
2008-05-15 23:34:41 +00:00
if ( ret < 0 )
2008-08-19 22:39:54 +02:00
return strlen ( str ) ;
if ( ( size_t ) ret > size - 1 )
return size - 1 ;
2008-05-15 23:34:41 +00:00
2008-08-19 22:39:54 +02:00
return ( size_t ) ret ;
2007-10-28 19:13:50 +00:00
}
/* Truncate the specified string, but guarantee that the string
* returned still validates as UTF8 */
char * pa_truncate_utf8 ( char * c , size_t l ) {
pa_assert ( c ) ;
pa_assert ( pa_utf8_valid ( c ) ) ;
if ( strlen ( c ) < = l )
return c ;
c [ l ] = 0 ;
while ( l > 0 & & ! pa_utf8_valid ( c ) )
c [ - - l ] = 0 ;
return c ;
}
char * pa_getcwd ( void ) {
size_t l = 128 ;
for ( ; ; ) {
2008-08-19 22:39:54 +02:00
char * p = pa_xmalloc ( l ) ;
2007-10-28 19:13:50 +00:00
if ( getcwd ( p , l ) )
return p ;
2016-05-06 10:14:46 +05:30
if ( errno ! = ERANGE ) {
pa_xfree ( p ) ;
2007-10-28 19:13:50 +00:00
return NULL ;
2016-05-06 10:14:46 +05:30
}
2007-10-28 19:13:50 +00:00
pa_xfree ( p ) ;
l * = 2 ;
}
}
void * pa_will_need ( const void * p , size_t l ) {
# ifdef RLIMIT_MEMLOCK
struct rlimit rlim ;
# endif
const void * a ;
size_t size ;
2011-06-23 22:21:03 +02:00
int r = ENOTSUP ;
2007-10-28 19:13:50 +00:00
size_t bs ;
2016-08-18 01:06:47 +02:00
const size_t page_size = pa_page_size ( ) ;
2007-10-28 19:13:50 +00:00
pa_assert ( p ) ;
pa_assert ( l > 0 ) ;
a = PA_PAGE_ALIGN_PTR ( p ) ;
2008-08-19 22:39:54 +02:00
size = ( size_t ) ( ( const uint8_t * ) p + l - ( const uint8_t * ) a ) ;
2007-10-28 19:13:50 +00:00
# ifdef HAVE_POSIX_MADVISE
if ( ( r = posix_madvise ( ( void * ) a , size , POSIX_MADV_WILLNEED ) ) = = 0 ) {
pa_log_debug ( " posix_madvise() worked fine! " ) ;
return ( void * ) p ;
}
# endif
/* Most likely the memory was not mmap()ed from a file and thus
* madvise ( ) didn ' t work , so let ' s misuse mlock ( ) do page this
* stuff back into RAM . Yeah , let ' s fuck with the MM ! It ' s so
* inviting , the man page of mlock ( ) tells us : " All pages that
* contain a part of the specified address range are guaranteed to
* be resident in RAM when the call returns successfully . " */
# ifdef RLIMIT_MEMLOCK
pa_assert_se ( getrlimit ( RLIMIT_MEMLOCK , & rlim ) = = 0 ) ;
2016-08-18 01:06:47 +02:00
if ( rlim . rlim_cur < page_size ) {
2007-10-28 19:13:50 +00:00
pa_log_debug ( " posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s " , pa_cstrerror ( r ) ) ;
2008-08-09 03:46:46 +02:00
errno = EPERM ;
2007-10-28 19:13:50 +00:00
return ( void * ) p ;
}
2008-08-20 03:33:06 +03:00
bs = PA_PAGE_ALIGN ( ( size_t ) rlim . rlim_cur ) ;
2007-10-28 19:13:50 +00:00
# else
2016-08-18 01:06:47 +02:00
bs = page_size * 4 ;
2007-10-28 19:13:50 +00:00
# endif
pa_log_debug ( " posix_madvise() failed (or doesn't exist), trying mlock(): %s " , pa_cstrerror ( r ) ) ;
# ifdef HAVE_MLOCK
while ( size > 0 & & bs > 0 ) {
if ( bs > size )
bs = size ;
if ( mlock ( a , bs ) < 0 ) {
bs = PA_PAGE_ALIGN ( bs / 2 ) ;
continue ;
}
pa_assert_se ( munlock ( a , bs ) = = 0 ) ;
a = ( const uint8_t * ) a + bs ;
size - = bs ;
}
# endif
if ( bs < = 0 )
pa_log_debug ( " mlock() failed too (or doesn't exist), giving up: %s " , pa_cstrerror ( errno ) ) ;
else
pa_log_debug ( " mlock() worked fine! " ) ;
return ( void * ) p ;
}
void pa_close_pipe ( int fds [ 2 ] ) {
pa_assert ( fds ) ;
if ( fds [ 0 ] > = 0 )
pa_assert_se ( pa_close ( fds [ 0 ] ) = = 0 ) ;
if ( fds [ 1 ] > = 0 )
pa_assert_se ( pa_close ( fds [ 1 ] ) = = 0 ) ;
fds [ 0 ] = fds [ 1 ] = - 1 ;
}
2007-10-29 15:31:24 +00:00
char * pa_readlink ( const char * p ) {
2011-01-06 00:51:33 +01:00
# ifdef HAVE_READLINK
2007-10-29 15:31:24 +00:00
size_t l = 100 ;
for ( ; ; ) {
char * c ;
ssize_t n ;
2008-08-19 22:39:54 +02:00
c = pa_xmalloc ( l ) ;
2007-10-29 15:31:24 +00:00
if ( ( n = readlink ( p , c , l - 1 ) ) < 0 ) {
pa_xfree ( c ) ;
return NULL ;
}
2007-10-29 21:19:05 +00:00
if ( ( size_t ) n < l - 1 ) {
2007-11-04 14:11:53 +00:00
c [ n ] = 0 ;
2007-10-29 15:31:24 +00:00
return c ;
}
pa_xfree ( c ) ;
l * = 2 ;
}
2011-01-06 00:51:33 +01:00
# else
return NULL ;
# endif
2007-10-29 15:31:24 +00:00
}
2008-05-15 23:34:41 +00:00
int pa_close_all ( int except_fd , . . . ) {
va_list ap ;
2008-08-19 22:39:54 +02:00
unsigned n = 0 , i ;
int r , * p ;
2008-05-15 23:34:41 +00:00
va_start ( ap , except_fd ) ;
if ( except_fd > = 0 )
for ( n = 1 ; va_arg ( ap , int ) > = 0 ; n + + )
;
va_end ( ap ) ;
p = pa_xnew ( int , n + 1 ) ;
va_start ( ap , except_fd ) ;
i = 0 ;
if ( except_fd > = 0 ) {
2008-05-18 19:09:14 +00:00
int fd ;
2008-05-15 23:34:41 +00:00
p [ i + + ] = except_fd ;
2008-05-18 19:09:14 +00:00
while ( ( fd = va_arg ( ap , int ) ) > = 0 )
p [ i + + ] = fd ;
2008-05-15 23:34:41 +00:00
}
p [ i ] = - 1 ;
va_end ( ap ) ;
r = pa_close_allv ( p ) ;
2009-08-23 21:49:37 +02:00
pa_xfree ( p ) ;
2008-05-15 23:34:41 +00:00
return r ;
}
int pa_close_allv ( const int except_fds [ ] ) {
2011-01-06 00:51:33 +01:00
# ifndef OS_IS_WIN32
2008-05-15 23:34:41 +00:00
struct rlimit rl ;
2009-05-22 01:31:56 +02:00
int maxfd , fd ;
2008-05-15 23:34:41 +00:00
# ifdef __linux__
2009-06-25 12:07:12 +02:00
int saved_errno ;
2008-05-15 23:34:41 +00:00
DIR * d ;
if ( ( d = opendir ( " /proc/self/fd " ) ) ) {
struct dirent * de ;
while ( ( de = readdir ( d ) ) ) {
2013-06-27 19:28:09 +02:00
bool found ;
2008-05-15 23:34:41 +00:00
long l ;
char * e = NULL ;
int i ;
if ( de - > d_name [ 0 ] = = ' . ' )
continue ;
errno = 0 ;
l = strtol ( de - > d_name , & e , 10 ) ;
if ( errno ! = 0 | | ! e | | * e ) {
closedir ( d ) ;
errno = EINVAL ;
return - 1 ;
}
fd = ( int ) l ;
if ( ( long ) fd ! = l ) {
closedir ( d ) ;
errno = EINVAL ;
return - 1 ;
}
2008-05-18 19:09:14 +00:00
if ( fd < 3 )
2008-05-15 23:34:41 +00:00
continue ;
if ( fd = = dirfd ( d ) )
continue ;
2013-06-27 19:28:09 +02:00
found = false ;
2008-05-15 23:34:41 +00:00
for ( i = 0 ; except_fds [ i ] > = 0 ; i + + )
2008-05-18 19:09:14 +00:00
if ( except_fds [ i ] = = fd ) {
2013-06-27 19:28:09 +02:00
found = true ;
2008-05-18 19:09:14 +00:00
break ;
}
2008-05-15 23:34:41 +00:00
2008-05-18 19:09:14 +00:00
if ( found )
continue ;
if ( pa_close ( fd ) < 0 ) {
2008-05-15 23:34:41 +00:00
saved_errno = errno ;
closedir ( d ) ;
errno = saved_errno ;
return - 1 ;
}
}
closedir ( d ) ;
return 0 ;
}
# endif
2009-05-22 01:31:56 +02:00
if ( getrlimit ( RLIMIT_NOFILE , & rl ) > = 0 )
maxfd = ( int ) rl . rlim_max ;
else
maxfd = sysconf ( _SC_OPEN_MAX ) ;
2008-05-15 23:34:41 +00:00
2009-05-22 01:31:56 +02:00
for ( fd = 3 ; fd < maxfd ; fd + + ) {
2008-05-15 23:34:41 +00:00
int i ;
2013-06-27 19:28:09 +02:00
bool found ;
2008-05-15 23:34:41 +00:00
2013-06-27 19:28:09 +02:00
found = false ;
2008-05-15 23:34:41 +00:00
for ( i = 0 ; except_fds [ i ] > = 0 ; i + + )
2008-07-29 15:36:00 +02:00
if ( except_fds [ i ] = = fd ) {
2013-06-27 19:28:09 +02:00
found = true ;
2008-07-29 15:36:00 +02:00
break ;
}
2008-05-15 23:34:41 +00:00
2008-07-29 15:36:00 +02:00
if ( found )
continue ;
if ( pa_close ( fd ) < 0 & & errno ! = EBADF )
2008-05-15 23:34:41 +00:00
return - 1 ;
}
2011-01-06 00:51:33 +01:00
# endif /* !OS_IS_WIN32 */
2008-05-15 23:34:41 +00:00
return 0 ;
}
int pa_unblock_sigs ( int except , . . . ) {
va_list ap ;
2008-08-19 22:39:54 +02:00
unsigned n = 0 , i ;
int r , * p ;
2008-05-15 23:34:41 +00:00
va_start ( ap , except ) ;
if ( except > = 1 )
for ( n = 1 ; va_arg ( ap , int ) > = 0 ; n + + )
;
va_end ( ap ) ;
p = pa_xnew ( int , n + 1 ) ;
va_start ( ap , except ) ;
i = 0 ;
if ( except > = 1 ) {
2008-05-18 19:09:14 +00:00
int sig ;
2008-05-15 23:34:41 +00:00
p [ i + + ] = except ;
2008-05-18 19:09:14 +00:00
while ( ( sig = va_arg ( ap , int ) ) > = 0 )
p [ i + + ] = sig ;
2008-05-15 23:34:41 +00:00
}
p [ i ] = - 1 ;
va_end ( ap ) ;
r = pa_unblock_sigsv ( p ) ;
pa_xfree ( p ) ;
return r ;
}
int pa_unblock_sigsv ( const int except [ ] ) {
2011-01-06 00:51:33 +01:00
# ifndef OS_IS_WIN32
2008-05-15 23:34:41 +00:00
int i ;
sigset_t ss ;
if ( sigemptyset ( & ss ) < 0 )
return - 1 ;
for ( i = 0 ; except [ i ] > 0 ; i + + )
if ( sigaddset ( & ss , except [ i ] ) < 0 )
return - 1 ;
return sigprocmask ( SIG_SETMASK , & ss , NULL ) ;
2011-01-06 00:51:33 +01:00
# else
return 0 ;
# endif
2008-05-15 23:34:41 +00:00
}
int pa_reset_sigs ( int except , . . . ) {
va_list ap ;
2008-08-19 22:39:54 +02:00
unsigned n = 0 , i ;
int * p , r ;
2008-05-15 23:34:41 +00:00
va_start ( ap , except ) ;
if ( except > = 1 )
for ( n = 1 ; va_arg ( ap , int ) > = 0 ; n + + )
;
va_end ( ap ) ;
p = pa_xnew ( int , n + 1 ) ;
va_start ( ap , except ) ;
i = 0 ;
if ( except > = 1 ) {
2008-07-29 15:36:00 +02:00
int sig ;
2008-05-15 23:34:41 +00:00
p [ i + + ] = except ;
2008-07-29 15:36:00 +02:00
while ( ( sig = va_arg ( ap , int ) ) > = 0 )
2009-09-08 23:52:58 +02:00
p [ i + + ] = sig ;
2008-05-15 23:34:41 +00:00
}
p [ i ] = - 1 ;
va_end ( ap ) ;
r = pa_reset_sigsv ( p ) ;
pa_xfree ( p ) ;
return r ;
}
int pa_reset_sigsv ( const int except [ ] ) {
2011-01-06 00:51:33 +01:00
# ifndef OS_IS_WIN32
2008-05-15 23:34:41 +00:00
int sig ;
2008-10-01 01:31:56 +02:00
for ( sig = 1 ; sig < NSIG ; sig + + ) {
2013-06-27 19:28:09 +02:00
bool reset = true ;
2008-05-15 23:34:41 +00:00
switch ( sig ) {
case SIGKILL :
case SIGSTOP :
2013-06-27 19:28:09 +02:00
reset = false ;
2008-05-15 23:34:41 +00:00
break ;
default : {
int i ;
for ( i = 0 ; except [ i ] > 0 ; i + + ) {
if ( sig = = except [ i ] ) {
2013-06-27 19:28:09 +02:00
reset = false ;
2008-05-15 23:34:41 +00:00
break ;
}
}
}
}
if ( reset ) {
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_handler = SIG_DFL ;
/* On Linux the first two RT signals are reserved by
* glibc , and sigaction ( ) will return EINVAL for them . */
if ( ( sigaction ( sig , & sa , NULL ) < 0 ) )
if ( errno ! = EINVAL )
return - 1 ;
}
}
2011-01-06 00:51:33 +01:00
# endif
2008-05-15 23:34:41 +00:00
return 0 ;
}
void pa_set_env ( const char * key , const char * value ) {
pa_assert ( key ) ;
pa_assert ( value ) ;
2009-09-02 00:34:27 +02:00
/* This is not thread-safe */
2011-01-06 02:10:45 +01:00
# ifdef OS_IS_WIN32
SetEnvironmentVariable ( key , value ) ;
# else
2011-01-12 08:15:44 +01:00
setenv ( key , value , 1 ) ;
2011-01-06 02:10:45 +01:00
# endif
2008-05-15 23:34:41 +00:00
}
2008-05-21 22:39:40 +00:00
2014-10-02 12:14:09 +03:00
void pa_unset_env ( const char * key ) {
pa_assert ( key ) ;
/* This is not thread-safe */
# ifdef OS_IS_WIN32
SetEnvironmentVariable ( key , NULL ) ;
# else
unsetenv ( key ) ;
# endif
}
2009-09-02 00:34:27 +02:00
void pa_set_env_and_record ( const char * key , const char * value ) {
pa_assert ( key ) ;
pa_assert ( value ) ;
/* This is not thread-safe */
pa_set_env ( key , value ) ;
recorded_env = pa_strlist_prepend ( recorded_env , key ) ;
}
void pa_unset_env_recorded ( void ) {
/* This is not thread-safe */
for ( ; ; ) {
2009-09-02 04:05:34 +02:00
char * s ;
2009-09-02 00:34:27 +02:00
2009-09-02 04:05:34 +02:00
recorded_env = pa_strlist_pop ( recorded_env , & s ) ;
if ( ! s )
2009-09-02 00:34:27 +02:00
break ;
2014-10-02 12:14:09 +03:00
pa_unset_env ( s ) ;
2009-09-02 00:34:27 +02:00
pa_xfree ( s ) ;
}
}
2013-06-27 19:28:09 +02:00
bool pa_in_system_mode ( void ) {
2008-05-21 22:39:40 +00:00
const char * e ;
if ( ! ( e = getenv ( " PULSE_SYSTEM " ) ) )
2013-06-27 19:28:09 +02:00
return false ;
2008-05-21 22:39:40 +00:00
return ! ! atoi ( e ) ;
}
2008-08-07 02:22:57 +02:00
2011-10-27 12:49:18 +02:00
/* Checks a whitespace-separated list of words in haystack for needle */
2013-06-27 19:28:09 +02:00
bool pa_str_in_list_spaces ( const char * haystack , const char * needle ) {
2011-10-27 12:49:18 +02:00
char * s ;
const char * state = NULL ;
if ( ! haystack | | ! needle )
2013-06-27 19:28:09 +02:00
return false ;
2011-10-27 12:49:18 +02:00
while ( ( s = pa_split_spaces ( haystack , & state ) ) ) {
if ( pa_streq ( needle , s ) ) {
pa_xfree ( s ) ;
2013-06-27 19:28:09 +02:00
return true ;
2011-10-27 12:49:18 +02:00
}
pa_xfree ( s ) ;
}
2013-06-27 19:28:09 +02:00
return false ;
2011-10-27 12:49:18 +02:00
}
2009-04-29 01:54:44 +02:00
char * pa_get_user_name_malloc ( void ) {
ssize_t k ;
char * u ;
2008-09-03 18:30:27 +02:00
2009-04-29 01:54:44 +02:00
# ifdef _SC_LOGIN_NAME_MAX
k = ( ssize_t ) sysconf ( _SC_LOGIN_NAME_MAX ) ;
2008-08-07 02:22:57 +02:00
2009-04-29 01:54:44 +02:00
if ( k < = 0 )
# endif
k = 32 ;
2008-08-07 02:22:57 +02:00
2009-04-29 01:54:44 +02:00
u = pa_xnew ( char , k + 1 ) ;
2008-09-03 18:30:27 +02:00
2009-04-29 01:54:44 +02:00
if ( ! ( pa_get_user_name ( u , k ) ) ) {
pa_xfree ( u ) ;
return NULL ;
2008-08-07 02:22:57 +02:00
}
2009-04-29 01:54:44 +02:00
return u ;
}
char * pa_get_host_name_malloc ( void ) {
size_t l ;
2008-08-07 02:22:57 +02:00
2008-09-03 18:30:27 +02:00
l = 100 ;
2008-08-07 02:22:57 +02:00
for ( ; ; ) {
char * c ;
2008-08-19 22:39:54 +02:00
c = pa_xmalloc ( l ) ;
2008-08-07 02:22:57 +02:00
if ( ! pa_get_host_name ( c , l ) ) {
2008-09-03 18:30:27 +02:00
if ( errno ! = EINVAL & & errno ! = ENAMETOOLONG )
break ;
} else if ( strlen ( c ) < l - 1 ) {
2009-04-13 22:21:08 +02:00
char * u ;
2008-09-03 18:30:27 +02:00
if ( * c = = 0 ) {
2008-08-07 02:22:57 +02:00
pa_xfree ( c ) ;
2008-09-03 18:30:27 +02:00
break ;
2008-08-07 02:22:57 +02:00
}
2009-04-13 22:21:08 +02:00
u = pa_utf8_filter ( c ) ;
pa_xfree ( c ) ;
return u ;
2008-09-03 18:30:27 +02:00
}
2008-08-07 02:22:57 +02:00
/* Hmm, the hostname is as long the space we offered the
* function , we cannot know if it fully fit in , so let ' s play
* safe and retry . */
pa_xfree ( c ) ;
l * = 2 ;
}
2008-09-03 18:30:27 +02:00
2009-04-29 01:54:44 +02:00
return NULL ;
}
char * pa_machine_id ( void ) {
FILE * f ;
char * h ;
/* The returned value is supposed be some kind of ascii identifier
2015-08-12 11:37:00 +02:00
* that is unique and stable across reboots . First we try if the machine - id
* file is available . If it ' s available , that ' s great , since it provides an
* identifier that suits our needs perfectly . If it ' s not , we fall back to
* the hostname , which is not as good , since it can change over time . */
/* We search for the machine-id file from four locations. The first two are
* relative to the configured installation prefix , but if we ' re installed
* under / usr / local , for example , it ' s likely that the machine - id won ' t be
* found there , so we also try the hardcoded paths .
*
* PA_MACHINE_ID or PA_MACHINE_ID_FALLBACK might exist on a Windows system ,
* but the last two hardcoded paths certainly don ' t , hence we don ' t try
* them on Windows . */
2011-11-14 13:42:21 +05:30
if ( ( f = pa_fopen_cloexec ( PA_MACHINE_ID , " r " ) ) | |
2015-06-09 21:56:36 +02:00
( f = pa_fopen_cloexec ( PA_MACHINE_ID_FALLBACK , " r " ) ) | |
# if !defined(OS_IS_WIN32)
( f = pa_fopen_cloexec ( " /etc/machine-id " , " r " ) ) | |
( f = pa_fopen_cloexec ( " /var/lib/dbus/machine-id " , " r " ) )
# else
false
# endif
) {
2009-04-29 01:54:44 +02:00
char ln [ 34 ] = " " , * r ;
r = fgets ( ln , sizeof ( ln ) - 1 , f ) ;
fclose ( f ) ;
pa_strip_nl ( ln ) ;
if ( r & & ln [ 0 ] )
return pa_utf8_filter ( ln ) ;
}
if ( ( h = pa_get_host_name_malloc ( ) ) )
return h ;
2013-09-18 16:22:00 +02:00
# if !defined(OS_IS_WIN32) && !defined(__ANDROID__)
2008-09-03 18:30:27 +02:00
/* If no hostname was set we use the POSIX hostid. It's usually
2009-04-13 22:21:08 +02:00
* the IPv4 address . Might not be that stable . */
2012-05-15 14:12:38 +02:00
return pa_sprintf_malloc ( " %08lx " , ( unsigned long ) gethostid ( ) ) ;
2011-01-06 00:51:33 +01:00
# else
return NULL ;
# endif
2008-09-05 15:42:39 +03:00
}
2009-04-13 22:20:48 +02:00
char * pa_session_id ( void ) {
const char * e ;
2012-05-15 14:04:31 +02:00
e = getenv ( " XDG_SESSION_ID " ) ;
if ( ! e )
2009-04-13 22:20:48 +02:00
return NULL ;
return pa_utf8_filter ( e ) ;
}
2008-09-05 15:42:39 +03:00
char * pa_uname_string ( void ) {
2011-01-06 02:10:45 +01:00
# ifdef HAVE_UNAME
2008-09-05 15:42:39 +03:00
struct utsname u ;
2009-02-26 16:48:58 +11:00
pa_assert_se ( uname ( & u ) > = 0 ) ;
2008-09-03 18:30:27 +02:00
2008-09-05 15:42:39 +03:00
return pa_sprintf_malloc ( " %s %s %s %s " , u . sysname , u . machine , u . release , u . version ) ;
2011-01-06 02:10:45 +01:00
# endif
# ifdef OS_IS_WIN32
OSVERSIONINFO i ;
pa_zero ( i ) ;
i . dwOSVersionInfoSize = sizeof ( OSVERSIONINFO ) ;
pa_assert_se ( GetVersionEx ( & i ) ) ;
return pa_sprintf_malloc ( " Windows %d.%d (%d) % s " , i.dwMajorVersion, i.dwMinorVersion, i.dwBuildNumber, i.szCSDVersion) ;
# endif
2008-08-07 02:22:57 +02:00
}
2008-10-04 00:10:43 +02:00
# ifdef HAVE_VALGRIND_MEMCHECK_H
2013-06-27 19:28:09 +02:00
bool pa_in_valgrind ( void ) {
2008-10-04 00:10:43 +02:00
static int b = 0 ;
/* To make heisenbugs a bit simpler to find we check for $VALGRIND
* here instead of really checking whether we run in valgrind or
* not . */
if ( b < 1 )
b = getenv ( " VALGRIND " ) ? 2 : 1 ;
return b > 1 ;
}
# endif
2009-01-10 02:53:57 +01:00
unsigned pa_gcd ( unsigned a , unsigned b ) {
while ( b > 0 ) {
unsigned t = b ;
b = a % b ;
a = t ;
}
return a ;
}
void pa_reduce ( unsigned * num , unsigned * den ) {
unsigned gcd = pa_gcd ( * num , * den ) ;
if ( gcd < = 0 )
return ;
* num / = gcd ;
* den / = gcd ;
pa_assert ( pa_gcd ( * num , * den ) = = 1 ) ;
}
2009-01-22 02:16:53 +01:00
unsigned pa_ncpus ( void ) {
long ncpus ;
2016-07-04 20:23:17 +02:00
# ifdef _SC_NPROCESSORS_ONLN
ncpus = sysconf ( _SC_NPROCESSORS_ONLN ) ;
2009-01-22 02:16:53 +01:00
# else
ncpus = 1 ;
# endif
return ncpus < = 0 ? 1 : ( unsigned ) ncpus ;
}
2009-02-04 17:19:15 +01:00
char * pa_replace ( const char * s , const char * a , const char * b ) {
pa_strbuf * sb ;
size_t an ;
pa_assert ( s ) ;
pa_assert ( a ) ;
2016-05-16 19:51:05 +02:00
pa_assert ( * a ) ;
2009-02-04 17:19:15 +01:00
pa_assert ( b ) ;
an = strlen ( a ) ;
sb = pa_strbuf_new ( ) ;
for ( ; ; ) {
const char * p ;
if ( ! ( p = strstr ( s , a ) ) )
break ;
pa_strbuf_putsn ( sb , s , p - s ) ;
pa_strbuf_puts ( sb , b ) ;
s = p + an ;
}
pa_strbuf_puts ( sb , s ) ;
2015-06-12 18:17:07 +05:30
return pa_strbuf_to_string_free ( sb ) ;
2009-02-04 17:19:15 +01:00
}
2009-07-21 00:02:27 +03:00
char * pa_escape ( const char * p , const char * chars ) {
const char * s ;
const char * c ;
pa_strbuf * buf = pa_strbuf_new ( ) ;
for ( s = p ; * s ; + + s ) {
if ( * s = = ' \\ ' )
pa_strbuf_putc ( buf , ' \\ ' ) ;
else if ( chars ) {
for ( c = chars ; * c ; + + c ) {
if ( * s = = * c ) {
pa_strbuf_putc ( buf , ' \\ ' ) ;
break ;
}
}
}
pa_strbuf_putc ( buf , * s ) ;
}
2015-06-12 18:17:07 +05:30
return pa_strbuf_to_string_free ( buf ) ;
2009-07-21 00:02:27 +03:00
}
2009-02-04 17:19:15 +01:00
char * pa_unescape ( char * p ) {
char * s , * d ;
2013-06-27 19:28:09 +02:00
bool escaped = false ;
2009-02-04 17:19:15 +01:00
for ( s = p , d = p ; * s ; s + + ) {
if ( ! escaped & & * s = = ' \\ ' ) {
2013-06-27 19:28:09 +02:00
escaped = true ;
2009-02-04 17:19:15 +01:00
continue ;
}
* ( d + + ) = * s ;
2013-06-27 19:28:09 +02:00
escaped = false ;
2009-02-04 17:19:15 +01:00
}
* d = 0 ;
return p ;
}
2009-02-18 21:57:57 +01:00
char * pa_realpath ( const char * path ) {
revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> > struct userdata {
> > pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> > pa_memchunk memchunk;
> >
> > - unsigned int page_size;
> > -
> > uint32_t frame_size;
> > - uint32_t buffer_size;
> > - unsigned int written_bytes, read_bytes;
> > + int32_t buffer_size;
> > + volatile uint64_t written_bytes, read_bytes;
> > + pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > + struct userdata *u;
> > + audio_info_t info;
> > +
> > + pa_assert_se(u = s->userdata);
> > +
> > + if (u->fd >= 0) {
> > + AUDIO_INITINFO(&info);
> > +
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > - info.play.error = 0;
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > + if (u->sink->thread_info.rewind_requested)
> > + pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> > err = ioctl(u->fd, AUDIO_GETINFO, &info);
> > pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > + case EINTR:
> > + break;
>
> I think you should simply try again in this case...
Done.
> > + case EAGAIN:
> > + u->buffer_size = u->buffer_size * 18 / 25;
> > + u->buffer_size -= u->buffer_size % u->frame_size;
> > + u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > + pa_sink_set_max_request(u->sink, u->buffer_size);
> > + pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > + break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > + pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > + } else {
> > + pa_rtpoll_set_timer_disabled(u->rtpoll);
> > }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > + u->frame_size = pa_frame_size(&ss);
> >
> > - if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > + u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
2009-03-07 16:48:10 +11:00
char * t ;
2009-02-18 21:57:57 +01:00
pa_assert ( path ) ;
2011-08-24 18:24:46 +02:00
/* We want only absolute paths */
2009-02-18 21:57:57 +01:00
if ( path [ 0 ] ! = ' / ' ) {
errno = EINVAL ;
return NULL ;
}
2012-05-29 09:21:54 +02:00
# if defined(__GLIBC__)
revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> > struct userdata {
> > pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> > pa_memchunk memchunk;
> >
> > - unsigned int page_size;
> > -
> > uint32_t frame_size;
> > - uint32_t buffer_size;
> > - unsigned int written_bytes, read_bytes;
> > + int32_t buffer_size;
> > + volatile uint64_t written_bytes, read_bytes;
> > + pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > + struct userdata *u;
> > + audio_info_t info;
> > +
> > + pa_assert_se(u = s->userdata);
> > +
> > + if (u->fd >= 0) {
> > + AUDIO_INITINFO(&info);
> > +
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > - info.play.error = 0;
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > + if (u->sink->thread_info.rewind_requested)
> > + pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> > err = ioctl(u->fd, AUDIO_GETINFO, &info);
> > pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > + case EINTR:
> > + break;
>
> I think you should simply try again in this case...
Done.
> > + case EAGAIN:
> > + u->buffer_size = u->buffer_size * 18 / 25;
> > + u->buffer_size -= u->buffer_size % u->frame_size;
> > + u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > + pa_sink_set_max_request(u->sink, u->buffer_size);
> > + pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > + break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > + pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > + } else {
> > + pa_rtpoll_set_timer_disabled(u->rtpoll);
> > }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > + u->frame_size = pa_frame_size(&ss);
> >
> > - if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > + u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
2009-03-07 16:48:10 +11:00
{
char * r ;
2009-02-18 21:57:57 +01:00
revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> > struct userdata {
> > pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> > pa_memchunk memchunk;
> >
> > - unsigned int page_size;
> > -
> > uint32_t frame_size;
> > - uint32_t buffer_size;
> > - unsigned int written_bytes, read_bytes;
> > + int32_t buffer_size;
> > + volatile uint64_t written_bytes, read_bytes;
> > + pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > + struct userdata *u;
> > + audio_info_t info;
> > +
> > + pa_assert_se(u = s->userdata);
> > +
> > + if (u->fd >= 0) {
> > + AUDIO_INITINFO(&info);
> > +
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > - info.play.error = 0;
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > + if (u->sink->thread_info.rewind_requested)
> > + pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> > err = ioctl(u->fd, AUDIO_GETINFO, &info);
> > pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > + case EINTR:
> > + break;
>
> I think you should simply try again in this case...
Done.
> > + case EAGAIN:
> > + u->buffer_size = u->buffer_size * 18 / 25;
> > + u->buffer_size -= u->buffer_size % u->frame_size;
> > + u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > + pa_sink_set_max_request(u->sink, u->buffer_size);
> > + pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > + break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > + pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > + } else {
> > + pa_rtpoll_set_timer_disabled(u->rtpoll);
> > }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > + u->frame_size = pa_frame_size(&ss);
> >
> > - if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > + u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
2009-03-07 16:48:10 +11:00
if ( ! ( r = realpath ( path , NULL ) ) )
return NULL ;
/* We copy this here in case our pa_xmalloc() is not
* implemented on top of libc malloc ( ) */
t = pa_xstrdup ( r ) ;
pa_xfree ( r ) ;
}
# elif defined(PATH_MAX)
{
char * path_buf ;
path_buf = pa_xmalloc ( PATH_MAX ) ;
2009-02-18 21:57:57 +01:00
2011-01-06 02:10:45 +01:00
# if defined(OS_IS_WIN32)
if ( ! ( t = _fullpath ( path_buf , path , _MAX_PATH ) ) ) {
pa_xfree ( path_buf ) ;
return NULL ;
}
# else
revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> > struct userdata {
> > pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> > pa_memchunk memchunk;
> >
> > - unsigned int page_size;
> > -
> > uint32_t frame_size;
> > - uint32_t buffer_size;
> > - unsigned int written_bytes, read_bytes;
> > + int32_t buffer_size;
> > + volatile uint64_t written_bytes, read_bytes;
> > + pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > + struct userdata *u;
> > + audio_info_t info;
> > +
> > + pa_assert_se(u = s->userdata);
> > +
> > + if (u->fd >= 0) {
> > + AUDIO_INITINFO(&info);
> > +
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > - info.play.error = 0;
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > + if (u->sink->thread_info.rewind_requested)
> > + pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> > err = ioctl(u->fd, AUDIO_GETINFO, &info);
> > pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > + case EINTR:
> > + break;
>
> I think you should simply try again in this case...
Done.
> > + case EAGAIN:
> > + u->buffer_size = u->buffer_size * 18 / 25;
> > + u->buffer_size -= u->buffer_size % u->frame_size;
> > + u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > + pa_sink_set_max_request(u->sink, u->buffer_size);
> > + pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > + break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > + pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > + } else {
> > + pa_rtpoll_set_timer_disabled(u->rtpoll);
> > }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > + u->frame_size = pa_frame_size(&ss);
> >
> > - if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > + u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
2009-03-07 16:48:10 +11:00
if ( ! ( t = realpath ( path , path_buf ) ) ) {
pa_xfree ( path_buf ) ;
return NULL ;
}
2011-01-06 02:10:45 +01:00
# endif
revive solaris module
On Wed, 4 Mar 2009, Lennart Poettering wrote:
[snip]
> > This patch disables link map/library versioning unless ld is GNU ld.
> > Another approach for solaris would be to use that linker's -M option,
> > but I couldn't make that work (due to undefined mainloop, browse and
> > simple symbols when linking pacat. I can post the errors if anyone is
> > intested.)
>
> The linking in PA is a bit weird since we have a cyclic dependency
> between libpulse and libpulsecommon which however is not explicit.
Could that affect the pacat link somehow?
What are the implications for client apps that link with the non-versioned
libraries I've been building on solaris?
[snip]
> > struct userdata {
> > pa_core *core;
> > @@ -87,15 +92,24 @@ struct userdata {
> >
> > pa_memchunk memchunk;
> >
> > - unsigned int page_size;
> > -
> > uint32_t frame_size;
> > - uint32_t buffer_size;
> > - unsigned int written_bytes, read_bytes;
> > + int32_t buffer_size;
> > + volatile uint64_t written_bytes, read_bytes;
> > + pa_mutex *written_bytes_lock;
>
> Hmm, we generally try do do things without locking in PA. This smells as
> if it was solvable using atomic ints as well.
>
> Actually, looking at this again I get the impression these mutex are
> completely unnecessary here. All functions that lock these mutexes are
> called from the IO thread so no locking should be nessary.
>
> Please don't use volatile here. I am pretty sure it is a misuse. Also
> see http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
> which applies here too I think.
OK, I've removed the locks. For some reason I thought that the get_latency
function was called from two different threads.
> > +static void sink_set_volume(pa_sink *s) {
> > + struct userdata *u;
> > + audio_info_t info;
> > +
> > + pa_assert_se(u = s->userdata);
> > +
> > + if (u->fd >= 0) {
> > + AUDIO_INITINFO(&info);
> > +
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> I'd prefer if you'd use pa_cvolume_max here instead of pa_cvolume_avg()
> because this makes the volume independant of the balance.
>
> > - info.play.error = 0;
> > + info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
> > + assert(info.play.gain <= AUDIO_MAX_GAIN);
>
> Same here. (i.e. for the source)
Done and done.
> > + if (u->sink->thread_info.rewind_requested)
> > + pa_sink_process_rewind(u->sink, 0);
>
> This is correct.
>
> >
> > err = ioctl(u->fd, AUDIO_GETINFO, &info);
> > pa_assert(err >= 0);
>
> Hmm, if at all this should be pa_assert_se(), not pa_assert() (so that
> it is not defined away by -DNDEBUG). However I'd prefer if the error
> would be could correctly. (I see that this code is not yours, but
> still...)
Done.
> > + case EINTR:
> > + break;
>
> I think you should simply try again in this case...
Done.
> > + case EAGAIN:
> > + u->buffer_size = u->buffer_size * 18 / 25;
> > + u->buffer_size -= u->buffer_size % u->frame_size;
> > + u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE);
> > + pa_sink_set_max_request(u->sink, u->buffer_size);
> > + pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);
> > + break;
>
> Hmm, care to explain this?
EAGAIN happens when the user requests a buffer size that is too large for
the STREAMS layer to accept. We end up looping with EAGAIN every time we
try to write out the rest of the buffer, which burns enough CPU time to
trip the CPU limit.
So, I reduce the buffer size with each EAGAIN. This gets us reasonably
close to the largest usable buffer size. (Perhaps there's a better way to
determine what that limit is, but I don't know how.)
> > +
> > + pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec));
> > + } else {
> > + pa_rtpoll_set_timer_disabled(u->rtpoll);
> > }
>
> Hmm, you schedule audio via timers? Is that a good idea?
Perhaps not. I won't know until I test on more hardware.
But, given that we have rt priority and high resolution timers on solaris,
I think it is OK in theory...
The reason I used a timer was to minimise CPU usage and avoid the CPU
limit. Recall that getting woken up by poll is not an option for playback
unfortunately. We can arrange for a signal when the FD becomes writable,
but that throws out the whole buffer size concept, which acts to reduce
latency.
> That really only makes sense if you have to deal with large buffers and
> support rewinding.
I've implemented rewind support, but I'm still not sure that I have
understood the concept; I take it that we "rewind" (from the point-of-view
of the renderer, not the sink) so that some rendered but as yet unplayed
portion of the memblock/buffers can then be rendered again?
> Please keep in mind that the system clock and the sound card clock
> deviate. If you use the system timers to do PCM scheduling ou might need
> a pa_smoother object that is able to estimate the deviation for you.
Actually, in an earlier version I did use a smoother (after reading about
that in the wiki). But because of the non-monotonic sample counter (bug?)
I decided that it probably wasn't worth the added complexity so I removed
it. I'll put the smoother back if I can figure out the problem with the
sample counter.
>
> > + u->frame_size = pa_frame_size(&ss);
> >
> > - if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
> > + u->buffer_size = 16384;
>
> It would appear more appropriate to me if the buffer size is adjusted by
> the sample spec used.
Done.
> One last thing: it would probably be a good idea to allocate a pa_card
> object and attach the sink and the source to it.
It is possible to open /dev/audio twice by loading the solaris module
twice -- once for the sink (passing record=0) and once for source (passing
playback=0), thus giving seperate threads/LWPs for source and sink. It
might be misleading to allocate two cards in that situation?
> Right now pa_cards are mostly useful for switching profiles but even if
> you do not allow switching profiles on-the-fly it is of some value to
> find out via the cards object which source belongs to which sink.
>
> Otherwise I am happy!
>
> Thanks for your patch! I'd be thankful if you could fix the issues
> pointed out and prepare another patch on top of current git!
No problem. Patch follows. It also includes a portability fix for
pa_realpath and a fix for a bug in the pa_signal_new() error path that
causes signal data be freed if you attempt to register the same signal
twice.
> I hope I answered all your questions,
Your answers were very helpful, thanks.
Finn
>
> Lennart
>
>
2009-03-07 16:48:10 +11:00
}
# else
# error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
# endif
2009-02-18 21:57:57 +01:00
return t ;
}
2009-05-25 23:56:38 +02:00
void pa_disable_sigpipe ( void ) {
# ifdef SIGPIPE
struct sigaction sa ;
pa_zero ( sa ) ;
if ( sigaction ( SIGPIPE , NULL , & sa ) < 0 ) {
pa_log ( " sigaction(): %s " , pa_cstrerror ( errno ) ) ;
return ;
}
sa . sa_handler = SIG_IGN ;
if ( sigaction ( SIGPIPE , & sa , NULL ) < 0 ) {
pa_log ( " sigaction(): %s " , pa_cstrerror ( errno ) ) ;
return ;
}
# endif
}
2009-06-17 03:13:01 +02:00
void pa_xfreev ( void * * a ) {
void * * p ;
if ( ! a )
return ;
for ( p = a ; * p ; p + + )
pa_xfree ( * p ) ;
pa_xfree ( a ) ;
}
2009-06-17 03:13:32 +02:00
char * * pa_split_spaces_strv ( const char * s ) {
char * * t , * e ;
unsigned i = 0 , n = 8 ;
const char * state = NULL ;
t = pa_xnew ( char * , n ) ;
while ( ( e = pa_split_spaces ( s , & state ) ) ) {
t [ i + + ] = e ;
if ( i > = n ) {
n * = 2 ;
t = pa_xrenew ( char * , t , n ) ;
}
}
if ( i < = 0 ) {
pa_xfree ( t ) ;
return NULL ;
}
t [ i ] = NULL ;
return t ;
}
2009-06-17 03:13:59 +02:00
char * pa_maybe_prefix_path ( const char * path , const char * prefix ) {
pa_assert ( path ) ;
if ( pa_is_path_absolute ( path ) )
return pa_xstrdup ( path ) ;
return pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , prefix , path ) ;
}
2009-08-01 01:59:58 +02:00
size_t pa_pipe_buf ( int fd ) {
# ifdef _PC_PIPE_BUF
long n ;
if ( ( n = fpathconf ( fd , _PC_PIPE_BUF ) ) > = 0 )
return ( size_t ) n ;
# endif
# ifdef PIPE_BUF
return PIPE_BUF ;
# else
return 4096 ;
# endif
}
2009-08-12 21:36:52 +02:00
void pa_reset_personality ( void ) {
2013-09-18 16:22:00 +02:00
# if defined(__linux__) && !defined(__ANDROID__)
2009-08-12 21:36:52 +02:00
if ( personality ( PER_LINUX ) < 0 )
pa_log_warn ( " Uh, personality() failed: %s " , pa_cstrerror ( errno ) ) ;
# endif
}
2009-08-21 03:43:53 +02:00
2013-06-27 19:28:09 +02:00
bool pa_run_from_build_tree ( void ) {
2009-08-21 03:43:53 +02:00
char * rp ;
2013-06-27 19:28:09 +02:00
static bool b = false ;
2009-08-21 03:43:53 +02:00
2013-04-20 10:12:34 +05:30
PA_ONCE_BEGIN {
if ( ( rp = pa_readlink ( " /proc/self/exe " ) ) ) {
b = pa_startswith ( rp , PA_BUILDDIR ) ;
pa_xfree ( rp ) ;
}
} PA_ONCE_END ;
2009-08-21 03:43:53 +02:00
return b ;
}
2009-09-17 21:06:54 +02:00
const char * pa_get_temp_dir ( void ) {
const char * t ;
if ( ( t = getenv ( " TMPDIR " ) ) & &
pa_is_path_absolute ( t ) )
return t ;
if ( ( t = getenv ( " TMP " ) ) & &
pa_is_path_absolute ( t ) )
return t ;
if ( ( t = getenv ( " TEMP " ) ) & &
pa_is_path_absolute ( t ) )
return t ;
if ( ( t = getenv ( " TEMPDIR " ) ) & &
pa_is_path_absolute ( t ) )
return t ;
return " /tmp " ;
}
2009-10-30 03:30:42 +01:00
int pa_open_cloexec ( const char * fn , int flags , mode_t mode ) {
int fd ;
# ifdef O_NOCTTY
flags | = O_NOCTTY ;
# endif
# ifdef O_CLOEXEC
if ( ( fd = open ( fn , flags | O_CLOEXEC , mode ) ) > = 0 )
2009-10-30 04:16:59 +01:00
goto finish ;
2009-10-30 03:30:42 +01:00
if ( errno ! = EINVAL )
return fd ;
# endif
2014-08-13 00:30:01 +02:00
if ( ( fd = open ( fn , flags , mode ) ) > = 0 )
goto finish ;
/* return error */
return fd ;
2009-10-30 03:30:42 +01:00
2009-10-30 04:16:59 +01:00
finish :
/* Some implementations might simply ignore O_CLOEXEC if it is not
* understood , make sure FD_CLOEXEC is enabled anyway */
2009-10-30 03:30:42 +01:00
pa_make_fd_cloexec ( fd ) ;
return fd ;
}
int pa_socket_cloexec ( int domain , int type , int protocol ) {
int fd ;
# ifdef SOCK_CLOEXEC
if ( ( fd = socket ( domain , type | SOCK_CLOEXEC , protocol ) ) > = 0 )
2009-10-30 04:16:59 +01:00
goto finish ;
2009-10-30 03:30:42 +01:00
if ( errno ! = EINVAL )
return fd ;
# endif
2014-08-13 00:30:01 +02:00
if ( ( fd = socket ( domain , type , protocol ) ) > = 0 )
goto finish ;
/* return error */
return fd ;
2009-10-30 03:30:42 +01:00
2009-10-30 04:16:59 +01:00
finish :
/* Some implementations might simply ignore SOCK_CLOEXEC if it is
* not understood , make sure FD_CLOEXEC is enabled anyway */
2009-10-30 03:30:42 +01:00
pa_make_fd_cloexec ( fd ) ;
return fd ;
}
int pa_pipe_cloexec ( int pipefd [ 2 ] ) {
int r ;
# ifdef HAVE_PIPE2
if ( ( r = pipe2 ( pipefd , O_CLOEXEC ) ) > = 0 )
2009-10-30 04:16:59 +01:00
goto finish ;
2009-10-30 03:30:42 +01:00
2016-06-30 12:38:14 +02:00
if ( errno = = EMFILE ) {
pa_log_error ( " The per-process limit on the number of open file descriptors has been reached. " ) ;
return r ;
}
if ( errno = = ENFILE ) {
pa_log_error ( " The system-wide limit on the total number of open files has been reached. " ) ;
return r ;
}
2009-10-30 03:30:42 +01:00
if ( errno ! = EINVAL & & errno ! = ENOSYS )
return r ;
2009-11-05 03:22:15 +01:00
2009-10-30 03:30:42 +01:00
# endif
2014-08-13 00:30:01 +02:00
if ( ( r = pipe ( pipefd ) ) > = 0 )
goto finish ;
2016-06-30 12:38:14 +02:00
if ( errno = = EMFILE ) {
pa_log_error ( " The per-process limit on the number of open file descriptors has been reached. " ) ;
return r ;
}
if ( errno = = ENFILE ) {
pa_log_error ( " The system-wide limit on the total number of open files has been reached. " ) ;
return r ;
}
2014-08-13 00:30:01 +02:00
/* return error */
return r ;
2009-10-30 03:30:42 +01:00
2009-10-30 04:16:59 +01:00
finish :
2009-10-30 03:30:42 +01:00
pa_make_fd_cloexec ( pipefd [ 0 ] ) ;
pa_make_fd_cloexec ( pipefd [ 1 ] ) ;
return 0 ;
}
int pa_accept_cloexec ( int sockfd , struct sockaddr * addr , socklen_t * addrlen ) {
int fd ;
2015-11-21 23:08:23 +01:00
errno = 0 ;
2009-10-30 03:30:42 +01:00
# ifdef HAVE_ACCEPT4
if ( ( fd = accept4 ( sockfd , addr , addrlen , SOCK_CLOEXEC ) ) > = 0 )
2009-10-30 04:20:24 +01:00
goto finish ;
2009-10-30 03:30:42 +01:00
if ( errno ! = EINVAL & & errno ! = ENOSYS )
return fd ;
2009-11-05 03:22:15 +01:00
2009-10-30 03:30:42 +01:00
# endif
2015-11-21 23:08:23 +01:00
# ifdef HAVE_PACCEPT
if ( ( fd = paccept ( sockfd , addr , addrlen , NULL , SOCK_CLOEXEC ) ) > = 0 )
goto finish ;
# endif
2014-08-13 00:30:01 +02:00
if ( ( fd = accept ( sockfd , addr , addrlen ) ) > = 0 )
goto finish ;
/* return error */
return fd ;
2009-10-30 03:30:42 +01:00
2009-10-30 04:20:24 +01:00
finish :
2009-10-30 03:30:42 +01:00
pa_make_fd_cloexec ( fd ) ;
2009-10-30 04:20:24 +01:00
return fd ;
}
2009-10-30 03:30:42 +01:00
2009-10-30 04:20:24 +01:00
FILE * pa_fopen_cloexec ( const char * path , const char * mode ) {
FILE * f ;
char * m ;
m = pa_sprintf_malloc ( " %se " , mode ) ;
errno = 0 ;
if ( ( f = fopen ( path , m ) ) ) {
pa_xfree ( m ) ;
goto finish ;
}
pa_xfree ( m ) ;
if ( errno ! = EINVAL )
return NULL ;
if ( ! ( f = fopen ( path , mode ) ) )
return NULL ;
finish :
pa_make_fd_cloexec ( fileno ( f ) ) ;
return f ;
2009-10-30 03:30:42 +01:00
}
2009-10-31 02:43:47 +01:00
void pa_nullify_stdfds ( void ) {
# ifndef OS_IS_WIN32
pa_close ( STDIN_FILENO ) ;
pa_close ( STDOUT_FILENO ) ;
pa_close ( STDERR_FILENO ) ;
pa_assert_se ( open ( " /dev/null " , O_RDONLY ) = = STDIN_FILENO ) ;
pa_assert_se ( open ( " /dev/null " , O_WRONLY ) = = STDOUT_FILENO ) ;
pa_assert_se ( open ( " /dev/null " , O_WRONLY ) = = STDERR_FILENO ) ;
# else
FreeConsole ( ) ;
# endif
}
2009-11-05 03:22:15 +01:00
char * pa_read_line_from_file ( const char * fn ) {
FILE * f ;
char ln [ 256 ] = " " , * r ;
if ( ! ( f = pa_fopen_cloexec ( fn , " r " ) ) )
return NULL ;
r = fgets ( ln , sizeof ( ln ) - 1 , f ) ;
fclose ( f ) ;
if ( ! r ) {
errno = EIO ;
return NULL ;
}
pa_strip_nl ( ln ) ;
return pa_xstrdup ( ln ) ;
}
2013-06-27 19:28:09 +02:00
bool pa_running_in_vm ( void ) {
2009-11-05 03:22:15 +01:00
# if defined(__i386__) || defined(__x86_64__)
/* Both CPUID and DMI are x86 specific interfaces... */
uint32_t eax = 0x40000000 ;
union {
uint32_t sig32 [ 3 ] ;
char text [ 13 ] ;
} sig ;
# ifdef __linux__
const char * const dmi_vendors [ ] = {
" /sys/class/dmi/id/sys_vendor " ,
" /sys/class/dmi/id/board_vendor " ,
" /sys/class/dmi/id/bios_vendor "
} ;
unsigned i ;
for ( i = 0 ; i < PA_ELEMENTSOF ( dmi_vendors ) ; i + + ) {
char * s ;
if ( ( s = pa_read_line_from_file ( dmi_vendors [ i ] ) ) ) {
if ( pa_startswith ( s , " QEMU " ) | |
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
pa_startswith ( s , " VMware " ) | |
pa_startswith ( s , " VMW " ) | |
pa_startswith ( s , " Microsoft Corporation " ) | |
pa_startswith ( s , " innotek GmbH " ) | |
pa_startswith ( s , " Xen " ) ) {
pa_xfree ( s ) ;
2013-06-27 19:28:09 +02:00
return true ;
2009-11-05 03:22:15 +01:00
}
pa_xfree ( s ) ;
}
}
# endif
/* http://lwn.net/Articles/301888/ */
pa_zero ( sig ) ;
__asm__ __volatile__ (
2009-11-11 04:50:32 +01:00
/* ebx/rbx is being used for PIC! */
" push %% " PA_REG_b " \n \t "
2009-11-05 03:22:15 +01:00
" cpuid \n \t "
2009-11-11 04:50:32 +01:00
" mov %%ebx, %1 \n \t "
" pop %% " PA_REG_b " \n \t "
2009-11-05 03:22:15 +01:00
2009-11-11 04:50:32 +01:00
: " =a " ( eax ) , " =r " ( sig . sig32 [ 0 ] ) , " =c " ( sig . sig32 [ 1 ] ) , " =d " ( sig . sig32 [ 2 ] )
2009-11-05 03:22:15 +01:00
: " 0 " ( eax )
) ;
if ( pa_streq ( sig . text , " XenVMMXenVMM " ) | |
pa_streq ( sig . text , " KVMKVMKVM " ) | |
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
pa_streq ( sig . text , " VMwareVMware " ) | |
/* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
pa_streq ( sig . text , " Microsoft Hv " ) )
2013-06-27 19:28:09 +02:00
return true ;
2009-11-05 03:22:15 +01:00
# endif
2013-06-27 19:28:09 +02:00
return false ;
2009-11-05 03:22:15 +01:00
}
2016-08-18 01:06:47 +02:00
size_t pa_page_size ( void ) {
# if defined(PAGE_SIZE)
return PAGE_SIZE ;
# elif defined(PAGESIZE)
return PAGESIZE ;
# elif defined(HAVE_SYSCONF)
static size_t page_size = 4096 ; /* Let's hope it's like x86. */
PA_ONCE_BEGIN {
long ret = sysconf ( _SC_PAGE_SIZE ) ;
if ( ret > 0 )
page_size = ret ;
} PA_ONCE_END ;
return page_size ;
# else
return 4096 ;
# endif
}