2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-04-15 05:11:11 +04:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 02:20:58 +04:00
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
2010-04-15 05:11:11 +04:00
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2010-04-15 05:11:11 +04:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2010-04-15 05:11:11 +04:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <string.h>
2010-10-06 04:33:40 +04:00
# include <assert.h>
2010-04-15 05:11:11 +04:00
2012-05-07 23:36:12 +04:00
# include "path-util.h"
2010-10-06 04:33:40 +04:00
# include "util.h"
2010-04-15 05:11:11 +04:00
# include "unit-name.h"
# define VALID_CHARS \
" 0123456789 " \
" abcdefghijklmnopqrstuvwxyz " \
" ABCDEFGHIJKLMNOPQRSTUVWXYZ " \
2010-04-15 08:19:54 +04:00
" :-_. \\ "
2010-04-15 05:11:11 +04:00
2012-06-06 18:56:18 +04:00
static const char * const unit_type_table [ _UNIT_TYPE_MAX ] = {
[ UNIT_SERVICE ] = " service " ,
[ UNIT_SOCKET ] = " socket " ,
[ UNIT_TARGET ] = " target " ,
[ UNIT_DEVICE ] = " device " ,
[ UNIT_MOUNT ] = " mount " ,
[ UNIT_AUTOMOUNT ] = " automount " ,
[ UNIT_SNAPSHOT ] = " snapshot " ,
[ UNIT_TIMER ] = " timer " ,
[ UNIT_SWAP ] = " swap " ,
[ UNIT_PATH ] = " path " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( unit_type , UnitType ) ;
2012-07-10 19:18:09 +04:00
static const char * const unit_load_state_table [ _UNIT_LOAD_STATE_MAX ] = {
[ UNIT_STUB ] = " stub " ,
[ UNIT_LOADED ] = " loaded " ,
[ UNIT_ERROR ] = " error " ,
[ UNIT_MERGED ] = " merged " ,
[ UNIT_MASKED ] = " masked "
} ;
DEFINE_STRING_TABLE_LOOKUP ( unit_load_state , UnitLoadState ) ;
2012-07-10 19:03:11 +04:00
bool unit_name_is_valid ( const char * n , bool template_ok ) {
2010-04-15 05:11:11 +04:00
const char * e , * i , * at ;
/* Valid formats:
*
* string @ instance . suffix
* string . suffix
*/
assert ( n ) ;
if ( strlen ( n ) > = UNIT_NAME_MAX )
return false ;
2010-10-06 04:33:40 +04:00
e = strrchr ( n , ' . ' ) ;
if ( ! e | | e = = n )
2010-04-15 05:11:11 +04:00
return false ;
2012-07-10 19:03:11 +04:00
if ( unit_type_from_string ( e + 1 ) < 0 )
return false ;
2010-04-15 05:11:11 +04:00
for ( i = n , at = NULL ; i < e ; i + + ) {
if ( * i = = ' @ ' & & ! at )
at = i ;
if ( ! strchr ( " @ " VALID_CHARS , * i ) )
return false ;
}
if ( at ) {
if ( at = = n )
return false ;
2010-10-08 05:09:25 +04:00
if ( ! template_ok & & at + 1 = = e )
2010-04-15 05:11:11 +04:00
return false ;
}
return true ;
}
bool unit_instance_is_valid ( const char * i ) {
assert ( i ) ;
/* The max length depends on the length of the string, so we
* don ' t really check this here . */
if ( i [ 0 ] = = 0 )
return false ;
/* We allow additional @ in the instance string, we do not
* allow them in the prefix ! */
for ( ; * i ; i + + )
if ( ! strchr ( " @ " VALID_CHARS , * i ) )
return false ;
return true ;
}
bool unit_prefix_is_valid ( const char * p ) {
/* We don't allow additional @ in the instance string */
if ( p [ 0 ] = = 0 )
return false ;
for ( ; * p ; p + + )
if ( ! strchr ( VALID_CHARS , * p ) )
return false ;
return true ;
}
int unit_name_to_instance ( const char * n , char * * instance ) {
const char * p , * d ;
char * i ;
assert ( n ) ;
assert ( instance ) ;
/* Everything past the first @ and before the last . is the instance */
2012-06-25 22:16:15 +04:00
p = strchr ( n , ' @ ' ) ;
if ( ! p ) {
2010-04-15 05:11:11 +04:00
* instance = NULL ;
return 0 ;
}
assert_se ( d = strrchr ( n , ' . ' ) ) ;
assert ( p < d ) ;
2012-06-25 22:16:15 +04:00
i = strndup ( p + 1 , d - p - 1 ) ;
if ( ! i )
2010-04-15 05:11:11 +04:00
return - ENOMEM ;
* instance = i ;
return 0 ;
}
char * unit_name_to_prefix_and_instance ( const char * n ) {
const char * d ;
assert ( n ) ;
assert_se ( d = strrchr ( n , ' . ' ) ) ;
return strndup ( n , d - n ) ;
}
char * unit_name_to_prefix ( const char * n ) {
const char * p ;
2012-06-22 15:08:48 +04:00
p = strchr ( n , ' @ ' ) ;
if ( p )
2010-04-15 05:11:11 +04:00
return strndup ( n , p - n ) ;
return unit_name_to_prefix_and_instance ( n ) ;
}
char * unit_name_change_suffix ( const char * n , const char * suffix ) {
char * e , * r ;
size_t a , b ;
assert ( n ) ;
2012-07-10 19:03:11 +04:00
assert ( unit_name_is_valid ( n , true ) ) ;
2010-04-15 05:11:11 +04:00
assert ( suffix ) ;
assert_se ( e = strrchr ( n , ' . ' ) ) ;
a = e - n ;
b = strlen ( suffix ) ;
2012-06-22 15:08:48 +04:00
r = new ( char , a + b + 1 ) ;
if ( ! r )
2010-04-15 05:11:11 +04:00
return NULL ;
memcpy ( r , n , a ) ;
memcpy ( r + a , suffix , b + 1 ) ;
return r ;
}
char * unit_name_build ( const char * prefix , const char * instance , const char * suffix ) {
assert ( prefix ) ;
assert ( unit_prefix_is_valid ( prefix ) ) ;
assert ( ! instance | | unit_instance_is_valid ( instance ) ) ;
assert ( suffix ) ;
if ( ! instance )
return strappend ( prefix , suffix ) ;
2012-07-13 15:41:01 +04:00
return strjoin ( prefix , " @ " , instance , suffix , NULL ) ;
2010-04-15 05:11:11 +04:00
}
2012-06-04 16:57:24 +04:00
static char * do_escape_char ( char c , char * t ) {
* ( t + + ) = ' \\ ' ;
* ( t + + ) = ' x ' ;
* ( t + + ) = hexchar ( c > > 4 ) ;
* ( t + + ) = hexchar ( c ) ;
return t ;
}
static char * do_escape ( const char * f , char * t ) {
2010-04-15 05:11:11 +04:00
assert ( f ) ;
assert ( t ) ;
2012-06-04 16:57:24 +04:00
/* do not create units with a leading '.', like for "/.dotdir" mount points */
if ( * f = = ' . ' ) {
t = do_escape_char ( * f , t ) ;
f + + ;
}
2010-04-15 05:11:11 +04:00
for ( ; * f ; f + + ) {
if ( * f = = ' / ' )
2010-04-17 01:24:39 +04:00
* ( t + + ) = ' - ' ;
2012-06-04 16:57:24 +04:00
else if ( * f = = ' - ' | | * f = = ' \\ ' | | ! strchr ( VALID_CHARS , * f ) )
t = do_escape_char ( * f , t ) ;
else
2010-04-15 05:11:11 +04:00
* ( t + + ) = * f ;
}
return t ;
}
char * unit_name_escape ( const char * f ) {
char * r , * t ;
2012-06-22 15:08:48 +04:00
r = new ( char , strlen ( f ) * 4 + 1 ) ;
if ( ! r )
2010-04-15 05:11:11 +04:00
return NULL ;
t = do_escape ( f , r ) ;
* t = 0 ;
return r ;
}
char * unit_name_unescape ( const char * f ) {
char * r , * t ;
assert ( f ) ;
2012-06-22 15:08:48 +04:00
r = strdup ( f ) ;
if ( ! r )
2010-04-15 05:11:11 +04:00
return NULL ;
for ( t = r ; * f ; f + + ) {
2010-04-17 01:24:39 +04:00
if ( * f = = ' - ' )
2010-04-15 05:11:11 +04:00
* ( t + + ) = ' / ' ;
else if ( * f = = ' \\ ' ) {
int a , b ;
2011-06-28 04:53:15 +04:00
if ( f [ 1 ] ! = ' x ' | |
( a = unhexchar ( f [ 2 ] ) ) < 0 | |
( b = unhexchar ( f [ 3 ] ) ) < 0 ) {
/* Invalid escape code, let's take it literal then */
2010-04-15 05:11:11 +04:00
* ( t + + ) = ' \\ ' ;
} else {
* ( t + + ) = ( char ) ( ( a < < 4 ) | b ) ;
2010-10-17 02:11:23 +04:00
f + = 3 ;
2010-04-15 05:11:11 +04:00
}
} else
* ( t + + ) = * f ;
}
* t = 0 ;
return r ;
}
2012-06-25 22:16:15 +04:00
char * unit_name_path_escape ( const char * f ) {
char * p , * e ;
assert ( f ) ;
p = strdup ( f ) ;
if ( ! p )
return NULL ;
path_kill_slashes ( p ) ;
if ( streq ( p , " / " ) ) {
free ( p ) ;
return strdup ( " - " ) ;
}
e = unit_name_escape ( p [ 0 ] = = ' / ' ? p + 1 : p ) ;
free ( p ) ;
return e ;
}
char * unit_name_path_unescape ( const char * f ) {
char * e ;
assert ( f ) ;
e = unit_name_unescape ( f ) ;
if ( ! e )
return NULL ;
if ( e [ 0 ] ! = ' / ' ) {
char * w ;
w = strappend ( " / " , e ) ;
free ( e ) ;
return w ;
}
return e ;
}
2010-04-15 05:11:11 +04:00
bool unit_name_is_template ( const char * n ) {
const char * p ;
assert ( n ) ;
if ( ! ( p = strchr ( n , ' @ ' ) ) )
return false ;
return p [ 1 ] = = ' . ' ;
}
char * unit_name_replace_instance ( const char * f , const char * i ) {
const char * p , * e ;
char * r , * k ;
size_t a ;
assert ( f ) ;
p = strchr ( f , ' @ ' ) ;
assert_se ( e = strrchr ( f , ' . ' ) ) ;
a = p - f ;
if ( p ) {
size_t b ;
b = strlen ( i ) ;
2012-06-25 22:16:15 +04:00
r = new ( char , a + 1 + b + strlen ( e ) + 1 ) ;
if ( ! r )
2010-04-15 05:11:11 +04:00
return NULL ;
k = mempcpy ( r , f , a + 1 ) ;
k = mempcpy ( k , i , b ) ;
} else {
2012-06-25 22:16:15 +04:00
r = new ( char , a + strlen ( e ) + 1 ) ;
if ( ! r )
2010-04-15 05:11:11 +04:00
return NULL ;
k = mempcpy ( r , f , a ) ;
}
strcpy ( k , e ) ;
return r ;
}
char * unit_name_template ( const char * f ) {
const char * p , * e ;
char * r ;
size_t a ;
2012-06-22 15:08:48 +04:00
p = strchr ( f , ' @ ' ) ;
if ( ! p )
2010-04-15 05:11:11 +04:00
return strdup ( f ) ;
assert_se ( e = strrchr ( f , ' . ' ) ) ;
a = p - f + 1 ;
2012-06-22 15:08:48 +04:00
r = new ( char , a + strlen ( e ) + 1 ) ;
if ( ! r )
2010-04-15 05:11:11 +04:00
return NULL ;
strcpy ( mempcpy ( r , f , a ) , e ) ;
return r ;
}
2010-04-21 05:27:44 +04:00
char * unit_name_from_path ( const char * path , const char * suffix ) {
2010-05-14 04:05:57 +04:00
char * p , * r ;
2010-04-21 05:27:44 +04:00
assert ( path ) ;
assert ( suffix ) ;
2012-06-25 22:16:15 +04:00
p = unit_name_path_escape ( path ) ;
2012-06-22 15:08:48 +04:00
if ( ! p )
2010-05-14 04:05:57 +04:00
return NULL ;
2012-06-25 22:16:15 +04:00
r = strappend ( p , suffix ) ;
2010-05-14 04:05:57 +04:00
free ( p ) ;
2010-04-21 05:27:44 +04:00
2010-05-14 04:05:57 +04:00
return r ;
2010-04-21 05:27:44 +04:00
}
2010-10-13 05:57:04 +04:00
char * unit_name_from_path_instance ( const char * prefix , const char * path , const char * suffix ) {
char * p , * r ;
2012-06-25 22:16:15 +04:00
assert ( prefix ) ;
2010-10-13 05:57:04 +04:00
assert ( path ) ;
assert ( suffix ) ;
2012-06-25 22:16:15 +04:00
p = unit_name_path_escape ( path ) ;
if ( ! p )
2010-10-13 05:57:04 +04:00
return NULL ;
2012-07-13 15:41:01 +04:00
r = strjoin ( prefix , " @ " , p , suffix , NULL ) ;
2010-10-13 05:57:04 +04:00
free ( p ) ;
return r ;
}
2010-04-21 05:27:44 +04:00
char * unit_name_to_path ( const char * name ) {
char * w , * e ;
assert ( name ) ;
2012-06-22 15:08:48 +04:00
w = unit_name_to_prefix ( name ) ;
if ( ! w )
2010-04-21 05:27:44 +04:00
return NULL ;
2012-06-25 22:16:15 +04:00
e = unit_name_path_unescape ( w ) ;
2010-04-21 05:27:44 +04:00
free ( w ) ;
2010-10-20 18:16:45 +04:00
return e ;
}
2012-06-13 20:22:08 +04:00
char * unit_dbus_path_from_name ( const char * name ) {
char * e , * p ;
2012-06-25 22:16:15 +04:00
assert ( name ) ;
2012-06-13 20:22:08 +04:00
e = bus_path_escape ( name ) ;
if ( ! e )
return NULL ;
p = strappend ( " /org/freedesktop/systemd1/unit/ " , e ) ;
free ( e ) ;
return p ;
}
2012-06-22 15:08:48 +04:00
char * unit_name_mangle ( const char * name ) {
char * r , * t ;
const char * f ;
assert ( name ) ;
/* Try to turn a string that might not be a unit name into a
* sensible unit name . */
if ( path_startswith ( name , " /dev/ " ) | |
path_startswith ( name , " /sys/ " ) )
return unit_name_from_path ( name , " .device " ) ;
if ( path_is_absolute ( name ) )
return unit_name_from_path ( name , " .mount " ) ;
/* We'll only escape the obvious characters here, to play
* safe . */
r = new ( char , strlen ( name ) * 4 + 1 ) ;
if ( ! r )
return NULL ;
for ( f = name , t = r ; * f ; f + + ) {
if ( * f = = ' / ' )
* ( t + + ) = ' - ' ;
else if ( ! strchr ( " @ " VALID_CHARS , * f ) )
t = do_escape_char ( * f , t ) ;
else
* ( t + + ) = * f ;
}
* t = 0 ;
return r ;
}
2012-07-10 19:03:11 +04:00
UnitType unit_name_to_type ( const char * n ) {
const char * e ;
assert ( n ) ;
e = strrchr ( n , ' . ' ) ;
if ( ! e )
return _UNIT_TYPE_INVALID ;
return unit_type_from_string ( e + 1 ) ;
}