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
2006-06-19 21:53:48 +00:00
License along with PulseAudio ; if not , write to the Free Software
2004-07-16 19:56:36 +00:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
2004-07-16 19:16:42 +00:00
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
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 <time.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>
2004-08-22 21:13:58 +00:00
# include <sys/time.h>
2008-05-15 23:34:41 +00:00
# include <dirent.h>
2008-08-09 03:46:23 +02:00
# include <regex.h>
# include <langinfo.h>
2008-09-05 15:42:39 +03:00
# include <sys/utsname.h>
2006-01-10 17:51:06 +00:00
2007-10-28 19:13:50 +00:00
# ifdef HAVE_STRTOF_L
# include <locale.h>
# endif
2006-01-10 17:51:06 +00:00
# ifdef HAVE_SCHED_H
2004-09-01 00:23:51 +00:00
# include <sched.h>
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
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__
# include <xlocale.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>
# include <pulsecore/winsock.h>
# 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>
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
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2006-06-19 23:56:54 +00:00
# define PULSE_ROOTENV "PULSE_ROOT"
2006-01-10 17:51:06 +00:00
int pa_set_root ( HANDLE handle ) {
2006-06-19 23:56:54 +00:00
char library_path [ MAX_PATH + sizeof ( PULSE_ROOTENV ) + 1 ] , * sep ;
2006-01-10 17:51:06 +00:00
2006-06-19 23:56:54 +00:00
strcpy ( library_path , PULSE_ROOTENV " = " ) ;
2006-01-10 17:51:06 +00:00
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno */
2006-06-19 23:56:54 +00:00
if ( ! GetModuleFileName ( handle , library_path + sizeof ( PULSE_ROOTENV ) , MAX_PATH ) )
2006-01-10 17:51:06 +00:00
return 0 ;
2007-10-28 19:13:50 +00:00
sep = strrchr ( library_path , PA_PATH_SEP_CHAR ) ;
2006-01-10 17:51:06 +00:00
if ( sep )
* sep = ' \0 ' ;
if ( _putenv ( library_path ) < 0 )
return 0 ;
return 1 ;
}
# endif
2004-11-11 21:18:33 +00:00
2004-11-04 18:57:31 +00:00
/** Make a file descriptor nonblock. Doesn't do any error checking */
2007-10-28 19:13:50 +00:00
void pa_make_fd_nonblock ( int fd ) {
2006-01-10 17:51:06 +00:00
# ifdef O_NONBLOCK
2004-06-23 23:17:30 +00:00
int v ;
2007-10-28 19:13:50 +00:00
pa_assert ( fd > = 0 ) ;
pa_assert_se ( ( v = fcntl ( fd , F_GETFL ) ) > = 0 ) ;
if ( ! ( v & O_NONBLOCK ) )
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)
u_long arg = 1 ;
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! " ) ;
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
}
/* 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
}
2004-11-04 18:57:31 +00:00
/** Creates a directory securely */
2006-07-19 17:44:19 +00:00
int pa_make_secure_dir ( const char * dir , mode_t m , uid_t uid , gid_t gid ) {
2004-06-29 16:48:37 +00:00
struct stat st ;
2008-08-09 03:46:46 +02:00
int r , saved_errno ;
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
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
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 ) ;
}
2006-01-10 17:51:06 +00:00
# endif
2007-01-04 13:43:45 +00:00
2006-07-19 17:44:19 +00:00
if ( r < 0 & & errno ! = EEXIST )
return - 1 ;
2006-01-10 17:51:06 +00:00
2006-04-24 15:07:09 +00:00
# ifdef HAVE_CHOWN
2006-07-20 00:21:50 +00:00
if ( uid = = ( uid_t ) - 1 )
uid = getuid ( ) ;
if ( gid = = ( gid_t ) - 1 )
gid = getgid ( ) ;
2007-10-28 19:13:50 +00:00
( void ) chown ( dir , uid , gid ) ;
2006-04-24 15:07:09 +00:00
# endif
2007-01-04 13:43:45 +00:00
2006-04-24 15:07:09 +00:00
# ifdef HAVE_CHMOD
2006-07-19 17:44:19 +00:00
chmod ( dir , m ) ;
2006-04-24 15:07:09 +00:00
# endif
2007-01-04 13:43:45 +00:00
2006-01-10 17:51:06 +00:00
# ifdef HAVE_LSTAT
if ( lstat ( dir , & st ) < 0 )
# else
if ( stat ( dir , & st ) < 0 )
# endif
2004-06-29 16:48:37 +00:00
goto fail ;
2007-01-04 13:43:45 +00:00
2006-01-10 17:51:06 +00:00
# ifndef OS_IS_WIN32
2006-07-19 17:44:19 +00:00
if ( ! S_ISDIR ( st . st_mode ) | |
( st . st_uid ! = uid ) | |
( st . st_gid ! = gid ) | |
( ( st . st_mode & 0777 ) ! = m ) ) {
errno = EACCES ;
2004-06-29 16:48:37 +00:00
goto fail ;
2006-07-19 17:44:19 +00:00
}
2006-01-10 17:51:06 +00:00
# else
2008-05-15 23:34:41 +00:00
pa_log_warn ( " Secure directory creation not supported on Win32. " ) ;
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 */
2006-07-19 17:44:19 +00:00
int pa_make_secure_parent_dir ( const char * fn , mode_t m , uid_t uid , gid_t gid ) {
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
2006-07-19 17:44:19 +00:00
if ( pa_make_secure_dir ( dir , m , uid , gid ) < 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 ;
2006-05-11 11:08:58 +00:00
if ( WSAGetLastError ( ) ! = WSAENOTSOCK ) {
errno = WSAGetLastError ( ) ;
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
if ( WSAGetLastError ( ) ! = WSAENOTSOCK ) {
errno = WSAGetLastError ( ) ;
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 ;
}
2007-02-14 12:13:49 +00:00
/** Platform independent read function. Necessary since not all
* 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 ;
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 , . . . ) {
2008-08-19 22:39:54 +02: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 ) {
2008-08-19 22:39:54 +02: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 ) {
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
strncpy ( b , s , l ) ;
b [ l - 1 ] = 0 ;
return b ;
}
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
2007-10-28 19:13:50 +00:00
# ifdef _POSIX_PRIORITY_SCHEDULING
struct sched_param sp ;
int r , policy ;
memset ( & sp , 0 , sizeof ( sp ) ) ;
policy = 0 ;
if ( ( r = pthread_getschedparam ( pthread_self ( ) , & policy , & sp ) ) ! = 0 ) {
pa_log ( " pthread_getschedgetparam(): %s " , pa_cstrerror ( r ) ) ;
2007-11-01 02:58:26 +00:00
return - 1 ;
}
if ( policy = = SCHED_FIFO & & sp . sched_priority > = rtprio ) {
pa_log_info ( " Thread already being scheduled with SCHED_FIFO with priority %i. " , sp . sched_priority ) ;
return 0 ;
2007-10-28 19:13:50 +00:00
}
2007-11-01 02:58:26 +00:00
sp . sched_priority = rtprio ;
2007-10-28 19:13:50 +00:00
if ( ( r = pthread_setschedparam ( pthread_self ( ) , SCHED_FIFO , & sp ) ) ! = 0 ) {
2007-11-01 02:58:26 +00:00
while ( sp . sched_priority > 1 ) {
sp . sched_priority - - ;
if ( ( r = pthread_setschedparam ( pthread_self ( ) , SCHED_FIFO , & sp ) ) = = 0 ) {
pa_log_info ( " Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i. " , sp . sched_priority , rtprio ) ;
return 0 ;
}
}
2007-10-28 19:13:50 +00:00
pa_log_warn ( " pthread_setschedparam(): %s " , pa_cstrerror ( r ) ) ;
2007-11-01 02:58:26 +00:00
return - 1 ;
2007-05-25 20:35:30 +00:00
}
2007-10-28 19:13:50 +00:00
2007-11-01 02:58:26 +00:00
pa_log_info ( " Successfully enabled SCHED_FIFO scheduling for thread, with priority %i. " , sp . sched_priority ) ;
return 0 ;
# else
2008-08-09 03:46:46 +02:00
errno = ENOTSUP ;
2007-11-01 02:58:26 +00:00
return - 1 ;
2007-05-25 20:35:30 +00:00
# endif
2007-10-28 19:13:50 +00:00
}
2008-05-15 23:34:41 +00:00
/* This is merely used for giving the user a hint. This is not correct
* for anything security related */
pa_bool_t pa_can_realtime ( void ) {
if ( geteuid ( ) = = 0 )
return TRUE ;
# if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
{
struct rlimit rl ;
if ( getrlimit ( RLIMIT_RTPRIO , & rl ) > = 0 )
if ( rl . rlim_cur > 0 | | rl . rlim_cur = = RLIM_INFINITY )
return TRUE ;
}
# endif
# if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
{
cap_t cap ;
if ( ( cap = cap_get_proc ( ) ) ) {
cap_flag_value_t flag = CAP_CLEAR ;
if ( cap_get_flag ( cap , CAP_SYS_NICE , CAP_EFFECTIVE , & flag ) > = 0 )
if ( flag = = CAP_SET ) {
cap_free ( cap ) ;
return TRUE ;
}
cap_free ( cap ) ;
}
}
# endif
return FALSE ;
}
/* This is merely used for giving the user a hint. This is not correct
* for anything security related */
pa_bool_t pa_can_high_priority ( void ) {
if ( geteuid ( ) = = 0 )
return TRUE ;
# if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
{
struct rlimit rl ;
if ( getrlimit ( RLIMIT_NICE , & rl ) > = 0 )
if ( rl . rlim_cur > = 21 | | rl . rlim_cur = = RLIM_INFINITY )
return TRUE ;
}
# endif
# if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
{
cap_t cap ;
if ( ( cap = cap_get_proc ( ) ) ) {
cap_flag_value_t flag = CAP_CLEAR ;
if ( cap_get_flag ( cap , CAP_SYS_NICE , CAP_EFFECTIVE , & flag ) > = 0 )
if ( flag = = CAP_SET ) {
cap_free ( cap ) ;
return TRUE ;
}
cap_free ( cap ) ;
}
}
# endif
return FALSE ;
}
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
2007-11-01 02:58:26 +00:00
if ( setpriority ( PRIO_PROCESS , 0 , nice_level ) < 0 ) {
int n ;
for ( n = nice_level + 1 ; n < 0 ; n + + ) {
if ( setpriority ( PRIO_PROCESS , 0 , n ) = = 0 ) {
pa_log_info ( " Successfully acquired nice level %i, which is lower than the requested %i. " , n , nice_level ) ;
return 0 ;
}
}
2006-08-18 21:38:40 +00:00
pa_log_warn ( " setpriority(): %s " , pa_cstrerror ( errno ) ) ;
2007-11-01 02:58:26 +00:00
return - 1 ;
}
pa_log_info ( " Successfully gained nice level %i. " , nice_level ) ;
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 ;
2007-11-01 02:58:26 +00:00
return . - 1 ;
} else
pa_log_info ( " Successfully gained high priority class. " ) ;
}
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
memset ( & sp , 0 , sizeof ( sp ) ) ;
pa_assert_se ( pthread_setschedparam ( pthread_self ( ) , SCHED_OTHER , & sp ) = = 0 ) ;
# 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 ) {
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 ;
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-08-09 03:46:23 +02:00
const char * expr ;
int r ;
2008-05-15 23:34:41 +00:00
pa_assert ( v ) ;
2007-01-04 13:43:45 +00:00
2008-08-09 03:46:23 +02:00
/* First we check language independant */
2004-09-20 17:19:35 +00:00
if ( ! strcmp ( v , " 1 " ) | | v [ 0 ] = = ' y ' | | v [ 0 ] = = ' Y ' | | v [ 0 ] = = ' t ' | | v [ 0 ] = = ' T ' | | ! strcasecmp ( v , " on " ) )
2004-09-13 23:28:30 +00:00
return 1 ;
2004-09-20 17:19:35 +00:00
else if ( ! strcmp ( v , " 0 " ) | | v [ 0 ] = = ' n ' | | v [ 0 ] = = ' N ' | | v [ 0 ] = = ' f ' | | v [ 0 ] = = ' F ' | | ! strcasecmp ( v , " off " ) )
2004-09-13 23:28:30 +00:00
return 0 ;
2008-08-09 03:46:23 +02:00
/* And then we check language dependant */
if ( ( expr = nl_langinfo ( YESEXPR ) ) )
if ( expr [ 0 ] )
2009-02-18 21:57:16 +01:00
if ( ( r = pa_match ( expr , v ) ) > 0 )
2008-08-09 03:46:23 +02:00
return 1 ;
if ( ( expr = nl_langinfo ( NOEXPR ) ) )
if ( expr [ 0 ] )
2009-02-18 21:57:16 +01:00
if ( ( r = pa_match ( expr , v ) ) > 0 )
2008-08-09 03:46:23 +02:00
return 0 ;
errno = EINVAL ;
2004-09-13 23:28:30 +00:00
return - 1 ;
}
2004-09-14 17:52:11 +00: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
* initiallized 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
2004-11-04 21:27:12 +00:00
/* What is interpreted as whitespace? */
2004-09-17 19:45:44 +00:00
# define WHITESPACE " \t\n"
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
2004-09-15 13:03:25 +00: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 ) {
struct group group , * result = NULL ;
2004-11-01 23:37:36 +00:00
long n ;
2004-09-23 22:42:49 +00:00
void * data ;
int r = - 1 ;
2004-11-01 23:37:36 +00:00
# ifdef HAVE_GETGRGID_R
# ifdef _SC_GETGR_R_SIZE_MAX
n = sysconf ( _SC_GETGR_R_SIZE_MAX ) ;
# else
n = - 1 ;
# endif
2009-01-22 02:15:50 +01:00
if ( n < = 0 )
2008-08-09 03:46:46 +02:00
n = 512 ;
2008-08-19 22:39:54 +02:00
data = pa_xmalloc ( ( size_t ) n ) ;
2004-09-23 22:42:49 +00:00
2008-08-09 03:46:46 +02:00
errno = 0 ;
2008-08-19 22:39:54 +02:00
if ( getgrgid_r ( gid , & group , data , ( size_t ) n , & result ) < 0 | | ! result ) {
2008-08-09 03:46:46 +02:00
pa_log ( " getgrgid_r(%u): %s " , ( unsigned ) gid , pa_cstrerror ( errno ) ) ;
if ( ! errno )
errno = ENOENT ;
2004-09-23 22:42:49 +00:00
goto finish ;
}
r = strcmp ( name , result - > gr_name ) = = 0 ;
2007-01-04 13:43:45 +00:00
2004-09-23 22:42:49 +00:00
finish :
pa_xfree ( data ) ;
2004-11-01 23:37:36 +00:00
# else
/* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
* support getgrgid_r . */
2008-08-09 03:46:46 +02:00
errno = 0 ;
2009-01-22 02:15:50 +01:00
if ( ! ( result = getgrgid ( gid ) ) ) {
2006-08-18 21:38:40 +00:00
pa_log ( " getgrgid(%u): %s " , gid , pa_cstrerror ( errno ) ) ;
2008-08-09 03:46:46 +02:00
if ( ! errno )
errno = ENOENT ;
2006-05-22 15:20:46 +00:00
goto finish ;
2004-11-01 23:37:36 +00:00
}
r = strcmp ( name , result - > gr_name ) = = 0 ;
finish :
# endif
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
2006-07-19 17:44:19 +00:00
/* Check whether the specifc 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 ) {
char * g_buf , * p_buf ;
long g_n , p_n ;
struct group grbuf , * gr ;
char * * i ;
int r = - 1 ;
2007-01-04 13:43:45 +00:00
2009-01-22 02:15:50 +01:00
# ifdef _SC_GETGR_R_SIZE_MAX
2006-02-24 17:14:23 +00:00
g_n = sysconf ( _SC_GETGR_R_SIZE_MAX ) ;
2009-01-22 02:15:50 +01:00
# else
g_n = - 1 ;
# endif
if ( g_n < = 0 )
g_n = 512 ;
2008-08-19 22:39:54 +02:00
g_buf = pa_xmalloc ( ( size_t ) g_n ) ;
2006-02-24 17:14:23 +00:00
2009-01-22 02:15:50 +01:00
# ifdef _SC_GETPW_R_SIZE_MAX
2006-02-24 17:14:23 +00:00
p_n = sysconf ( _SC_GETPW_R_SIZE_MAX ) ;
2009-01-22 02:15:50 +01:00
# else
p_n = - 1 ;
# endif
if ( p_n < = 0 )
p_n = 512 ;
2008-08-19 22:39:54 +02:00
p_buf = pa_xmalloc ( ( size_t ) p_n ) ;
2007-01-04 13:43:45 +00:00
2008-08-09 03:46:46 +02:00
errno = 0 ;
2009-01-22 02:15:50 +01:00
# ifdef HAVE_GETGRNAM_R
if ( getgrnam_r ( name , & grbuf , g_buf , ( size_t ) g_n , & gr ) ! = 0 | | ! gr )
# else
if ( ! ( gr = getgrnam ( name ) ) )
# endif
{
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 ;
for ( i = gr - > gr_mem ; * i ; i + + ) {
struct passwd pwbuf , * pw ;
2007-01-04 13:43:45 +00:00
2009-01-22 02:15:50 +01:00
# ifdef HAVE_GETPWNAM_R
2006-02-24 17:14:23 +00:00
if ( getpwnam_r ( * i , & pwbuf , p_buf , ( size_t ) p_n , & pw ) ! = 0 | | ! pw )
2009-01-22 02:15:50 +01:00
# else
if ( ! ( pw = getpwnam ( * i ) ) )
# endif
2006-02-24 17:14:23 +00:00
continue ;
if ( pw - > pw_uid = = uid ) {
r = 1 ;
break ;
}
}
finish :
pa_xfree ( g_buf ) ;
pa_xfree ( p_buf ) ;
return r ;
}
2006-07-19 17:44:19 +00:00
/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
gid_t pa_get_gid_of_group ( const char * name ) {
gid_t ret = ( gid_t ) - 1 ;
char * g_buf ;
long g_n ;
struct group grbuf , * gr ;
2009-01-22 02:15:50 +01:00
# ifdef _SC_GETGR_R_SIZE_MAX
2006-07-19 17:44:19 +00:00
g_n = sysconf ( _SC_GETGR_R_SIZE_MAX ) ;
2009-01-22 02:15:50 +01:00
# else
g_n = - 1 ;
# endif
if ( g_n < = 0 )
g_n = 512 ;
2008-08-19 22:39:54 +02:00
g_buf = pa_xmalloc ( ( size_t ) g_n ) ;
2006-07-19 17:44:19 +00:00
2008-08-09 03:46:46 +02:00
errno = 0 ;
2009-01-22 02:15:50 +01:00
# ifdef HAVE_GETGRNAM_R
if ( getgrnam_r ( name , & grbuf , g_buf , ( size_t ) g_n , & gr ) ! = 0 | | ! gr )
# else
if ( ! ( gr = getgrnam ( name ) ) )
# endif
{
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 :
pa_xfree ( g_buf ) ;
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 ) {
2008-08-09 03:46:46 +02:00
errno = ENOSUP ;
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 ) {
2008-08-09 03:46:46 +02:00
errno = ENOSUP ;
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 ) {
2008-08-09 03:46:46 +02:00
errno = ENOSUP ;
2006-07-19 21:48:35 +00:00
return ( gid_t ) - 1 ;
}
int pa_check_in_group ( gid_t g ) {
2008-08-09 03:46:46 +02:00
errno = ENOSUP ;
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
2004-09-27 15:40:18 +00:00
struct flock flock ;
2004-11-20 23:48:18 +00:00
/* Try a R/W lock first */
2007-01-04 13:43:45 +00:00
2008-08-19 22:39:54 +02:00
flock . l_type = ( short ) ( b ? F_WRLCK : F_UNLCK ) ;
2004-09-27 15:40:18 +00:00
flock . l_whence = SEEK_SET ;
flock . l_start = 0 ;
flock . l_len = 0 ;
2004-11-20 23:48:18 +00:00
if ( fcntl ( fd , F_SETLKW , & flock ) > = 0 )
return 0 ;
2004-09-27 15:40:18 +00:00
2004-11-20 23:48:18 +00:00
/* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
if ( b & & errno = = EBADF ) {
flock . l_type = F_RDLCK ;
if ( fcntl ( fd , F_SETLKW , & flock ) > = 0 )
return 0 ;
}
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_log ( " %slock: %s " , ! b ? " un " : " " , pa_cstrerror ( errno ) ) ;
2006-01-10 17:51:06 +00:00
# endif
# ifdef OS_IS_WIN32
HANDLE h = ( HANDLE ) _get_osfhandle ( fd ) ;
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
s [ strcspn ( s , " \r \n " ) ] = 0 ;
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 ) {
2004-11-21 13:18:56 +00:00
int fd = - 1 ;
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
2007-10-28 19:13:50 +00:00
if ( ( fd = open ( fn , O_CREAT | O_RDWR
# ifdef O_NOCTTY
| O_NOCTTY
# endif
# 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 ;
}
2004-09-28 22:47:48 +00:00
2004-11-21 13:18:56 +00:00
fd = - 1 ;
}
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 ;
}
2004-11-04 21:27:12 +00:00
/* Unlock a temporary lcok 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 ;
}
2008-08-07 02:25:48 +02:00
static char * get_pulse_home ( void ) {
char h [ PATH_MAX ] ;
struct stat st ;
2008-05-15 23:34:41 +00:00
2008-08-07 02:25:48 +02:00
if ( ! pa_get_home_dir ( h , sizeof ( h ) ) ) {
pa_log_error ( " Failed to get home directory. " ) ;
return NULL ;
}
2008-05-15 23:34:41 +00:00
2008-08-07 02:25:48 +02:00
if ( stat ( h , & st ) < 0 ) {
pa_log_error ( " Failed to stat home directory %s: %s " , h , pa_cstrerror ( errno ) ) ;
return NULL ;
}
2008-05-15 23:34:41 +00:00
2008-08-07 02:25:48 +02:00
if ( st . st_uid ! = getuid ( ) ) {
pa_log_error ( " Home directory %s not ours. " , h ) ;
2008-08-09 03:46:46 +02:00
errno = EACCES ;
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
return pa_sprintf_malloc ( " %s " PA_PATH_SEP " .pulse " , h ) ;
}
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
2008-08-19 22:39:54 +02:00
if ( pa_make_secure_dir ( d , 0700U , ( uid_t ) - 1 , ( gid_t ) - 1 ) < 0 ) {
2008-05-15 23:34:41 +00:00
pa_log_error ( " Failed to create secure directory: %s " , 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 ;
}
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
const char * tmpdir ;
char * fn ;
size_t pathlen ;
2008-08-07 02:25:48 +02:00
2008-09-23 00:26:31 +02:00
if ( ! ( tmpdir = getenv ( " TMPDIR " ) ) )
if ( ! ( tmpdir = getenv ( " TMP " ) ) )
if ( ! ( tmpdir = getenv ( " TEMP " ) ) )
tmpdir = getenv ( " TEMPDIR " ) ;
if ( ! tmpdir | | ! pa_is_path_absolute ( tmpdir ) )
tmpdir = " /tmp " ;
fn = pa_sprintf_malloc ( " %s/pulse-XXXXXXXXXXXX " , tmpdir ) ;
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 ) ;
r = mkdir ( fn , m ) ;
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 ;
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 ;
}
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 ;
struct stat st ;
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
* to be kept accross reboots and is usuallly private to the user ,
* except in system mode , where it might be accessible by other
* users , too . Since we need POSIX locking and UNIX sockets in
* this directory , we link it to a random subdir in / tmp , if it
* was not explicitly configured . */
2008-09-12 17:39:08 +03:00
m = pa_in_system_mode ( ) ? 0755U : 0700U ;
2008-08-07 02:25:48 +02:00
2008-09-12 17:39:08 +03:00
if ( ( d = getenv ( " PULSE_RUNTIME_PATH " ) ) ) {
2008-08-07 02:25:48 +02:00
2008-08-19 22:39:54 +02:00
if ( pa_make_secure_dir ( d , m , ( uid_t ) - 1 , ( gid_t ) - 1 ) < 0 ) {
2008-08-07 02:25:48 +02:00
pa_log_error ( " Failed to create secure directory: %s " , pa_cstrerror ( errno ) ) ;
goto fail ;
}
return pa_xstrdup ( d ) ;
}
if ( ! ( d = get_pulse_home ( ) ) )
goto fail ;
2008-09-12 17:39:08 +03:00
if ( pa_make_secure_dir ( d , m , ( uid_t ) - 1 , ( gid_t ) - 1 ) < 0 ) {
pa_log_error ( " Failed to create secure directory: %s " , pa_cstrerror ( errno ) ) ;
2009-02-18 21:21:25 +02:00
pa_xfree ( d ) ;
2008-09-12 17:39:08 +03:00
goto fail ;
}
2008-08-07 02:25:48 +02:00
if ( ! ( mid = pa_machine_id ( ) ) ) {
pa_xfree ( d ) ;
goto fail ;
}
k = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s:runtime " , d , mid ) ;
pa_xfree ( d ) ;
pa_xfree ( mid ) ;
for ( ; ; ) {
/* OK, first let's check if the "runtime" symlink is already
* existant */
if ( ! ( p = pa_readlink ( k ) ) ) {
if ( errno ! = ENOENT ) {
pa_log_error ( " Failed to stat runtime directory %s: %s " , k , pa_cstrerror ( errno ) ) ;
goto fail ;
}
/* 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 ;
}
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 ;
}
/* Hmm, so this symlink is still around, make sure nobody fools
* us */
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. " ) ;
}
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 ;
/* Hmm, someone lese was quicker then us. Let's give
* 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 ;
# ifdef OS_IS_WIN32
char buf [ PATH_MAX ] ;
2006-06-19 23:56:54 +00:00
if ( ! getenv ( PULSE_ROOTENV ) )
2006-01-10 17:51:06 +00:00
pa_set_root ( NULL ) ;
# endif
if ( env & & ( fn = getenv ( env ) ) ) {
2008-05-15 23:34:41 +00:00
FILE * f ;
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
if ( ! ExpandEnvironmentStrings ( fn , buf , PATH_MAX ) )
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2006-01-10 17:51:06 +00:00
return NULL ;
fn = buf ;
# endif
2008-05-15 23:34:41 +00:00
if ( ( f = fopen ( fn , " r " ) ) ) {
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 ;
char h [ PATH_MAX ] ;
FILE * f ;
2006-01-10 17:51:06 +00:00
2006-07-19 21:48:35 +00: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 ) ;
else if ( pa_get_home_dir ( h , sizeof ( h ) ) )
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .pulse " PA_PATH_SEP " %s " , h , local ) ;
2008-08-06 03:04:22 +02:00
else
return NULL ;
2008-05-15 23:34:41 +00:00
# ifdef OS_IS_WIN32
if ( ! ExpandEnvironmentStrings ( lfn , buf , PATH_MAX ) ) {
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2008-05-15 23:34:41 +00:00
pa_xfree ( lfn ) ;
return NULL ;
}
fn = buf ;
# endif
2007-11-24 16:22:23 +00:00
2008-05-15 23:34:41 +00:00
if ( ( f = fopen ( fn , " r " ) ) ) {
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 ) {
FILE * f ;
2007-01-04 13:43:45 +00:00
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2008-05-15 23:34:41 +00:00
if ( ! ExpandEnvironmentStrings ( global , buf , PATH_MAX ) )
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2008-05-15 23:34:41 +00:00
return NULL ;
global = buf ;
2006-01-10 17:51:06 +00:00
# endif
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
if ( ( f = fopen ( global , " r " ) ) ) {
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
if ( result )
* result = pa_xstrdup ( global ) ;
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
}
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 ;
# ifdef OS_IS_WIN32
char buf [ PATH_MAX ] ;
if ( ! getenv ( PULSE_ROOTENV ) )
pa_set_root ( NULL ) ;
# endif
if ( env & & ( fn = getenv ( env ) ) ) {
2008-08-06 03:04:22 +02:00
2008-05-15 23:34:41 +00:00
# ifdef OS_IS_WIN32
if ( ! ExpandEnvironmentStrings ( fn , buf , PATH_MAX ) )
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2008-05-15 23:34:41 +00:00
return NULL ;
fn = buf ;
# endif
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 ;
char h [ PATH_MAX ] ;
if ( ( e = getenv ( " PULSE_CONFIG_PATH " ) ) )
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , e , local ) ;
else if ( pa_get_home_dir ( h , sizeof ( h ) ) )
fn = lfn = pa_sprintf_malloc ( " %s " PA_PATH_SEP " .pulse " PA_PATH_SEP " %s " , h , local ) ;
2008-08-06 03:04:22 +02:00
else
return NULL ;
2008-05-15 23:34:41 +00:00
2006-01-10 17:51:06 +00:00
# ifdef OS_IS_WIN32
2008-05-15 23:34:41 +00:00
if ( ! ExpandEnvironmentStrings ( lfn , buf , PATH_MAX ) ) {
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2008-05-15 23:34:41 +00:00
pa_xfree ( lfn ) ;
return NULL ;
}
fn = buf ;
# endif
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 ) {
# ifdef OS_IS_WIN32
if ( ! ExpandEnvironmentStrings ( global , buf , PATH_MAX ) )
2008-08-09 03:46:46 +02:00
/* FIXME: Needs to set errno! */
2008-05-15 23:34:41 +00:00
return NULL ;
global = buf ;
2006-01-10 17:51:06 +00:00
# endif
2008-08-06 03:04:22 +02:00
if ( access ( global , R_OK ) = = 0 )
2008-05-15 23:34:41 +00:00
return pa_xstrdup ( global ) ;
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
while ( i < dlength & & j + 3 < = slength ) {
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 */
2008-05-21 22:39:40 +00:00
pa_bool_t 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 */
2008-05-21 22:39:40 +00:00
pa_bool_t 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 ) ;
return l1 > = l2 & & strcmp ( s + l1 - l2 , sfx ) = = 0 ;
}
2008-05-15 23:34:41 +00:00
pa_bool_t pa_is_path_absolute ( const char * fn ) {
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
2008-05-15 23:34:41 +00:00
/* if fn is null return the PulseAudio run time path in s (~/.pulse)
* if fn is non - null and starts with / return fn
* otherwise append fn to the run time path and return it */
2008-08-07 02:28:47 +02:00
static char * get_path ( const char * fn , pa_bool_t prependmid , pa_bool_t rt ) {
2008-05-15 23:34:41 +00:00
char * rtp ;
2006-01-10 17:51:06 +00:00
2008-05-15 23:34:41 +00:00
if ( pa_is_path_absolute ( fn ) )
return pa_xstrdup ( fn ) ;
2008-05-21 22:39:40 +00:00
rtp = rt ? pa_get_runtime_dir ( ) : pa_get_state_dir ( ) ;
if ( ! rtp )
return NULL ;
2008-05-15 23:34:41 +00:00
if ( fn ) {
char * r ;
2008-08-07 02:28:47 +02:00
if ( prependmid ) {
char * mid ;
if ( ! ( mid = pa_machine_id ( ) ) ) {
pa_xfree ( rtp ) ;
return NULL ;
}
r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s:%s " , rtp , mid , fn ) ;
pa_xfree ( mid ) ;
} else
r = pa_sprintf_malloc ( " %s " PA_PATH_SEP " %s " , rtp , fn ) ;
2008-05-15 23:34:41 +00:00
pa_xfree ( rtp ) ;
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 ) {
2008-08-07 02:28:47 +02:00
return get_path ( fn , FALSE , TRUE ) ;
2008-05-21 22:39:40 +00:00
}
2008-08-07 02:28:47 +02:00
char * pa_state_path ( const char * fn , pa_bool_t 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 ) {
char * x = NULL ;
long l ;
2007-10-28 19:13:50 +00:00
pa_assert ( s ) ;
pa_assert ( ret_i ) ;
errno = 0 ;
2004-12-11 00:10:41 +00:00
l = strtol ( s , & x , 0 ) ;
2008-08-09 03:46:46 +02:00
if ( ! x | | * x | | errno ) {
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 ( ( 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 ) ;
errno = 0 ;
2004-12-11 00:10:41 +00:00
l = strtoul ( s , & x , 0 ) ;
2008-08-09 03:46:46 +02:00
if ( ! x | | * x | | errno ) {
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
# ifdef HAVE_STRTOF_L
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
/* This should be locale independent */
# ifdef HAVE_STRTOF_L
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 ) ;
}
2008-08-09 03:46:46 +02:00
if ( ! x | | * x | | errno ) {
if ( ! errno )
errno = EINVAL ;
return - 1 ;
}
2007-10-28 19:13:50 +00:00
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 ;
if ( errno ! = ERANGE )
return NULL ;
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 ;
int r ;
size_t bs ;
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 ) ;
if ( rlim . rlim_cur < PA_PAGE_SIZE ) {
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
bs = PA_PAGE_SIZE * 4 ;
# 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 ) {
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 ;
}
}
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 ) ;
free ( p ) ;
return r ;
}
int pa_close_allv ( const int except_fds [ ] ) {
struct rlimit rl ;
2009-05-22 01:31:56 +02:00
int maxfd , fd ;
2008-05-15 23:34:41 +00:00
int saved_errno ;
# ifdef __linux__
DIR * d ;
if ( ( d = opendir ( " /proc/self/fd " ) ) ) {
struct dirent * de ;
while ( ( de = readdir ( d ) ) ) {
2008-05-18 19:09:14 +00:00
pa_bool_t 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 ;
2008-05-18 19:09:14 +00: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 ) {
found = TRUE ;
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 ;
2008-07-29 15:36:00 +02:00
pa_bool_t found ;
2008-05-15 23:34:41 +00:00
2008-07-29 15:36:00 +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 ) {
found = TRUE ;
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 ;
}
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 [ ] ) {
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 ) ;
}
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 )
sig = p [ i + + ] ;
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 [ ] ) {
int sig ;
2008-10-01 01:31:56 +02:00
for ( sig = 1 ; sig < NSIG ; sig + + ) {
2008-05-18 19:09:14 +00:00
pa_bool_t reset = TRUE ;
2008-05-15 23:34:41 +00:00
switch ( sig ) {
case SIGKILL :
case SIGSTOP :
2008-05-18 19:09:14 +00: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 ] ) {
2008-05-18 19:09:14 +00: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 ;
}
}
return 0 ;
}
void pa_set_env ( const char * key , const char * value ) {
pa_assert ( key ) ;
pa_assert ( value ) ;
putenv ( pa_sprintf_malloc ( " %s=%s " , key , value ) ) ;
}
2008-05-21 22:39:40 +00:00
pa_bool_t pa_in_system_mode ( void ) {
const char * e ;
if ( ! ( e = getenv ( " PULSE_SYSTEM " ) ) )
return FALSE ;
return ! ! atoi ( e ) ;
}
2008-08-07 02:22:57 +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
* that is unique and stable across reboots . */
/* First we try the D-Bus UUID, which is the best option we have,
* since it fits perfectly our needs and is not as volatile as the
* hostname which might be set from dhcp . */
if ( ( f = fopen ( PA_MACHINE_ID , " r " ) ) ) {
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 ;
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 . */
2008-09-03 18:30:27 +02:00
return pa_sprintf_malloc ( " %08lx " , ( unsigned long ) gethostid ) ;
2008-09-05 15:42:39 +03:00
}
2009-04-13 22:20:48 +02:00
char * pa_session_id ( void ) {
const char * e ;
if ( ! ( e = getenv ( " XDG_SESSION_COOKIE " ) ) )
return NULL ;
return pa_utf8_filter ( e ) ;
}
2008-09-05 15:42:39 +03:00
char * pa_uname_string ( void ) {
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 ) ;
2008-08-07 02:22:57 +02:00
}
2008-10-04 00:10:43 +02:00
# ifdef HAVE_VALGRIND_MEMCHECK_H
pa_bool_t pa_in_valgrind ( void ) {
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 ;
# ifdef _SC_NPROCESSORS_CONF
ncpus = sysconf ( _SC_NPROCESSORS_CONF ) ;
# 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 ) ;
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 ) ;
return pa_strbuf_tostring_free ( sb ) ;
}
char * pa_unescape ( char * p ) {
char * s , * d ;
pa_bool_t escaped = FALSE ;
for ( s = p , d = p ; * s ; s + + ) {
if ( ! escaped & & * s = = ' \\ ' ) {
escaped = TRUE ;
continue ;
}
* ( d + + ) = * s ;
escaped = FALSE ;
}
* 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 ) ;
/* We want only abolsute paths */
if ( path [ 0 ] ! = ' / ' ) {
errno = EINVAL ;
return NULL ;
}
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 defined(__GLIBC__) || defined(__APPLE__)
{
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
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 ;
}
}
# 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 ;
}