# include "common/params.h"
# ifndef _GNU_SOURCE
# define _GNU_SOURCE
# endif // _GNU_SOURCE
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <dirent.h>
# include <sys/file.h>
# include <sys/stat.h>
# include <map>
# include <string>
# include <string.h>
# include "common/util.h"
# include "common/utilpp.h"
namespace {
template < typename T >
T * null_coalesce ( T * a , T * b ) {
return a ! = NULL ? a : b ;
}
static const char * default_params_path = null_coalesce ( const_cast < const char * > ( getenv ( " PARAMS_PATH " ) ) , " /data/params " ) ;
# ifdef QCOM
static const char * persistent_params_path = null_coalesce ( const_cast < const char * > ( getenv ( " PERSISTENT_PARAMS_PATH " ) ) , " /persist/comma/params " ) ;
# else
static const char * persistent_params_path = default_params_path ;
# endif
} //namespace
static int fsync_dir ( const char * path ) {
int result = 0 ;
int fd = open ( path , O_RDONLY ) ;
if ( fd < 0 ) {
result = - 1 ;
goto cleanup ;
}
result = fsync ( fd ) ;
if ( result < 0 ) {
goto cleanup ;
}
cleanup :
int result_close = 0 ;
if ( fd > = 0 ) {
result_close = close ( fd ) ;
}
if ( result_close < 0 ) {
return result_close ;
} else {
return result ;
}
}
static int ensure_dir_exists ( const char * path ) {
struct stat st ;
if ( stat ( path , & st ) = = - 1 ) {
return mkdir ( path , 0700 ) ;
}
return 0 ;
}
int write_db_value ( const char * key , const char * value , size_t value_size , bool persistent_param ) {
// Information about safely and atomically writing a file: https://lwn.net/Articles/457667/
// 1) Create temp file
// 2) Write data to temp file
// 3) fsync() the temp file
// 4) rename the temp file to the real name
// 5) fsync() the containing directory
int lock_fd = - 1 ;
int tmp_fd = - 1 ;
int result ;
char tmp_path [ 1024 ] ;
char path [ 1024 ] ;
char * tmp_dir ;
ssize_t bytes_written ;
const char * params_path = persistent_param ? persistent_params_path : default_params_path ;
// Make sure params path exists
result = ensure_dir_exists ( params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
result = snprintf ( path , sizeof ( path ) , " %s/d " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
// See if the symlink exists, otherwise create it
struct stat st ;
if ( stat ( path , & st ) = = - 1 ) {
// Create temp folder
result = snprintf ( path , sizeof ( path ) , " %s/.tmp_XXXXXX " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
tmp_dir = mkdtemp ( path ) ;
if ( tmp_dir = = NULL ) {
goto cleanup ;
}
// Set permissions
result = chmod ( tmp_dir , 0777 ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Symlink it to temp link
result = snprintf ( tmp_path , sizeof ( tmp_path ) , " %s.link " , tmp_dir ) ;
if ( result < 0 ) {
goto cleanup ;
}
result = symlink ( tmp_dir , tmp_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Move symlink to <params>/d
result = snprintf ( path , sizeof ( path ) , " %s/d " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
result = rename ( tmp_path , path ) ;
if ( result < 0 ) {
goto cleanup ;
}
}
// Write value to temp.
result =
snprintf ( tmp_path , sizeof ( tmp_path ) , " %s/.tmp_value_XXXXXX " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
tmp_fd = mkstemp ( tmp_path ) ;
bytes_written = write ( tmp_fd , value , value_size ) ;
if ( bytes_written ! = value_size ) {
result = - 20 ;
goto cleanup ;
}
// Build lock path
result = snprintf ( path , sizeof ( path ) , " %s/.lock " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
lock_fd = open ( path , O_CREAT ) ;
// Build key path
result = snprintf ( path , sizeof ( path ) , " %s/d/%s " , params_path , key ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Take lock.
result = flock ( lock_fd , LOCK_EX ) ;
if ( result < 0 ) {
goto cleanup ;
}
// change permissions to 0666 for apks
result = fchmod ( tmp_fd , 0666 ) ;
if ( result < 0 ) {
goto cleanup ;
}
// fsync to force persist the changes.
result = fsync ( tmp_fd ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Move temp into place.
result = rename ( tmp_path , path ) ;
if ( result < 0 ) {
goto cleanup ;
}
// fsync parent directory
result = snprintf ( path , sizeof ( path ) , " %s/d " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
result = fsync_dir ( path ) ;
if ( result < 0 ) {
goto cleanup ;
}
cleanup :
// Release lock.
if ( lock_fd > = 0 ) {
close ( lock_fd ) ;
}
if ( tmp_fd > = 0 ) {
if ( result < 0 ) {
remove ( tmp_path ) ;
}
close ( tmp_fd ) ;
}
return result ;
}
int delete_db_value ( const char * key , bool persistent_param ) {
int lock_fd = - 1 ;
int result ;
char path [ 1024 ] ;
const char * params_path = persistent_param ? persistent_params_path : default_params_path ;
// Build lock path, and open lockfile
result = snprintf ( path , sizeof ( path ) , " %s/.lock " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
lock_fd = open ( path , O_CREAT ) ;
// Take lock.
result = flock ( lock_fd , LOCK_EX ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Build key path
result = snprintf ( path , sizeof ( path ) , " %s/d/%s " , params_path , key ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Delete value.
result = remove ( path ) ;
if ( result ! = 0 ) {
result = ERR_NO_VALUE ;
goto cleanup ;
}
// fsync parent directory
result = snprintf ( path , sizeof ( path ) , " %s/d " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
result = fsync_dir ( path ) ;
if ( result < 0 ) {
goto cleanup ;
}
cleanup :
// Release lock.
if ( lock_fd > = 0 ) {
close ( lock_fd ) ;
}
return result ;
}
int read_db_value ( const char * key , char * * value , size_t * value_sz , bool persistent_param ) {
int lock_fd = - 1 ;
int result ;
char path [ 1024 ] ;
const char * params_path = persistent_param ? persistent_params_path : default_params_path ;
result = snprintf ( path , sizeof ( path ) , " %s/.lock " , params_path ) ;
if ( result < 0 ) {
goto cleanup ;
}
lock_fd = open ( path , 0 ) ;
result = snprintf ( path , sizeof ( path ) , " %s/d/%s " , params_path , key ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Take lock.
result = flock ( lock_fd , LOCK_EX ) ;
if ( result < 0 ) {
goto cleanup ;
}
// Read value.
// TODO(mgraczyk): If there is a lot of contention, we can release the lock
// after opening the file, before reading.
* value = static_cast < char * > ( read_file ( path , value_sz ) ) ;
if ( * value = = NULL ) {
result = - 22 ;
goto cleanup ;
}
result = 0 ;
cleanup :
// Release lock.
if ( lock_fd > = 0 ) {
close ( lock_fd ) ;
}
return result ;
}
void read_db_value_blocking ( const char * key , char * * value , size_t * value_sz , bool persistent_param ) {
while ( 1 ) {
const int result = read_db_value ( key , value , value_sz , persistent_param ) ;
if ( result = = 0 ) {
return ;
} else {
// Sleep for 0.1 seconds.
usleep ( 100000 ) ;
}
}
}
int read_db_all ( std : : map < std : : string , std : : string > * params , bool persistent_param ) {
int err = 0 ;
const char * params_path = persistent_param ? persistent_params_path : default_params_path ;
std : : string lock_path = util : : string_format ( " %s/.lock " , params_path ) ;
int lock_fd = open ( lock_path . c_str ( ) , 0 ) ;
if ( lock_fd < 0 ) return - 1 ;
err = flock ( lock_fd , LOCK_EX ) ;
if ( err < 0 ) return err ;
std : : string key_path = util : : string_format ( " %s/d " , params_path ) ;
DIR * d = opendir ( key_path . c_str ( ) ) ;
if ( ! d ) {
close ( lock_fd ) ;
return - 1 ;
}
struct dirent * de = NULL ;
while ( ( de = readdir ( d ) ) ) {
if ( ! isalnum ( de - > d_name [ 0 ] ) ) continue ;
std : : string key = std : : string ( de - > d_name ) ;
std : : string value = util : : read_file ( util : : string_format ( " %s/%s " , key_path . c_str ( ) , key . c_str ( ) ) ) ;
( * params ) [ key ] = value ;
}
closedir ( d ) ;
close ( lock_fd ) ;
return 0 ;
}
std : : vector < char > read_db_bytes ( const char * param_name , bool persistent_param ) {
std : : vector < char > bytes ;
char * value ;
size_t sz ;
int result = read_db_value ( param_name , & value , & sz , persistent_param ) ;
if ( result = = 0 ) {
bytes . assign ( value , value + sz ) ;
free ( value ) ;
}
return bytes ;
}