2017-11-22 16:13:32 +03:00
#!/usr/bin/env python3
2020-11-09 07:23:58 +03:00
# SPDX-License-Identifier: LGPL-2.1-or-later
2017-11-22 16:13:32 +03:00
#
2018-12-09 21:17:33 +03:00
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
2017-11-22 16:13:32 +03:00
2017-11-23 15:56:32 +03:00
import os
2017-11-22 16:13:32 +03:00
import sys
2017-12-01 23:15:51 +03:00
import socket
2017-11-22 16:13:32 +03:00
import subprocess
2017-12-01 23:15:51 +03:00
import tempfile
import pwd
2018-10-13 11:26:48 +03:00
import grp
2017-12-01 23:15:51 +03:00
try :
from systemd import id128
except ImportError :
id128 = None
2017-11-22 16:13:32 +03:00
EX_DATAERR = 65 # from sysexits.h
2017-12-02 16:00:58 +03:00
EXIT_TEST_SKIP = 77
try :
subprocess . run
except AttributeError :
sys . exit ( EXIT_TEST_SKIP )
2017-11-22 16:13:32 +03:00
2017-12-06 22:04:45 +03:00
exe_with_args = sys . argv [ 1 : ]
2017-11-22 16:13:32 +03:00
2017-12-01 23:15:51 +03:00
def test_line ( line , * , user , returncode = EX_DATAERR , extra = { } ) :
2017-11-23 15:56:32 +03:00
args = [ ' --user ' ] if user else [ ]
2017-12-06 22:04:45 +03:00
print ( ' Running {} on {!r} ' . format ( ' ' . join ( exe_with_args + args ) , line ) )
c = subprocess . run ( exe_with_args + [ ' --create ' , ' - ' ] + args ,
2017-12-02 16:00:58 +03:00
input = line , stdout = subprocess . PIPE , universal_newlines = True ,
* * extra )
2017-11-23 15:56:32 +03:00
assert c . returncode == returncode , c
def test_invalids ( * , user ) :
test_line ( ' asdfa ' , user = user )
test_line ( ' f " open quote ' , user = user )
test_line ( ' f closed quote " " ' , user = user )
test_line ( ' Y /unknown/letter ' , user = user )
test_line ( ' w non/absolute/path ' , user = user )
test_line ( ' s ' , user = user ) # s is for short
test_line ( ' f!! /too/many/bangs ' , user = user )
test_line ( ' f++ /too/many/plusses ' , user = user )
test_line ( ' f+!+ /too/many/plusses ' , user = user )
test_line ( ' f!+! /too/many/bangs ' , user = user )
2021-03-30 17:37:11 +03:00
test_line ( ' f== /too/many/equals ' , user = user )
2017-11-23 15:56:32 +03:00
test_line ( ' w /unresolved/argument - - - - " % Y " ' , user = user )
test_line ( ' w /unresolved/argument/sandwich - - - - " % v % Y % v " ' , user = user )
test_line ( ' w /unresolved/filename/ % Y - - - - " whatever " ' , user = user )
test_line ( ' w /unresolved/filename/sandwich/ % v % Y % v - - - - " whatever " ' , user = user )
2019-04-27 03:22:40 +03:00
test_line ( ' w - - - - - " no file specified " ' , user = user )
test_line ( ' C - - - - - " no file specified " ' , user = user )
2017-11-23 15:56:32 +03:00
test_line ( ' C non/absolute/path - - - - - ' , user = user )
test_line ( ' b - - - - - - ' , user = user )
test_line ( ' b 1234 - - - - - ' , user = user )
test_line ( ' c - - - - - - ' , user = user )
test_line ( ' c 1234 - - - - - ' , user = user )
test_line ( ' t - - - ' , user = user )
test_line ( ' T - - - ' , user = user )
test_line ( ' a - - - ' , user = user )
test_line ( ' A - - - ' , user = user )
test_line ( ' h - - - ' , user = user )
test_line ( ' H - - - ' , user = user )
2018-06-03 19:57:21 +03:00
def test_uninitialized_t ( ) :
2017-11-23 15:56:32 +03:00
if os . getuid ( ) == 0 :
return
2017-12-01 23:15:51 +03:00
test_line ( ' w /foo - - - - " specifier for --user % t " ' ,
user = True , returncode = 0 , extra = { ' env ' : { } } )
2021-03-30 17:37:11 +03:00
def test_content ( line , expected , * , user , extra = { } , subpath = ' /arg ' , path_cb = None ) :
2017-12-01 23:15:51 +03:00
d = tempfile . TemporaryDirectory ( prefix = ' test-systemd-tmpfiles. ' )
2021-03-30 17:37:11 +03:00
if path_cb is not None :
path_cb ( d . name , subpath )
arg = d . name + subpath
2017-12-01 23:15:51 +03:00
spec = line . format ( arg )
test_line ( spec , user = user , returncode = 0 , extra = extra )
content = open ( arg ) . read ( )
print ( ' expect: {!r} \n actual: {!r} ' . format ( expected , content ) )
assert content == expected
def test_valid_specifiers ( * , user ) :
test_content ( ' f {} - - - - two words ' , ' two words ' , user = user )
if id128 :
2017-12-02 17:40:30 +03:00
try :
test_content ( ' f {} - - - - % m ' , ' {} ' . format ( id128 . get_machine ( ) . hex ) , user = user )
except AssertionError as e :
print ( e )
print ( ' /etc/machine-id: {!r} ' . format ( open ( ' /etc/machine-id ' ) . read ( ) ) )
print ( ' /proc/cmdline: {!r} ' . format ( open ( ' /proc/cmdline ' ) . read ( ) ) )
print ( ' skipping ' )
2017-12-01 23:15:51 +03:00
test_content ( ' f {} - - - - % b ' , ' {} ' . format ( id128 . get_boot ( ) . hex ) , user = user )
test_content ( ' f {} - - - - % H ' , ' {} ' . format ( socket . gethostname ( ) ) , user = user )
test_content ( ' f {} - - - - % v ' , ' {} ' . format ( os . uname ( ) . release ) , user = user )
test_content ( ' f {} - - - - % U ' , ' {} ' . format ( os . getuid ( ) ) , user = user )
2018-10-13 11:26:48 +03:00
test_content ( ' f {} - - - - %G ' , ' {} ' . format ( os . getgid ( ) ) , user = user )
2017-12-01 23:15:51 +03:00
2018-10-13 11:26:48 +03:00
puser = pwd . getpwuid ( os . getuid ( ) )
test_content ( ' f {} - - - - %u ' , ' {} ' . format ( puser . pw_name ) , user = user )
pgroup = grp . getgrgid ( os . getgid ( ) )
test_content ( ' f {} - - - - %g ' , ' {} ' . format ( pgroup . gr_name ) , user = user )
2017-12-04 11:05:05 +03:00
# Note that %h is the only specifier in which we look the environment,
# because we check $HOME. Should we even be doing that?
home = os . path . expanduser ( " ~ " )
test_content ( ' f {} - - - - % h ' , ' {} ' . format ( home ) , user = user )
2017-12-01 23:15:51 +03:00
xdg_runtime_dir = os . getenv ( ' XDG_RUNTIME_DIR ' )
if xdg_runtime_dir is not None or not user :
test_content ( ' f {} - - - - % t ' ,
xdg_runtime_dir if user else ' /run ' ,
user = user )
xdg_config_home = os . getenv ( ' XDG_CONFIG_HOME ' )
if xdg_config_home is not None or not user :
test_content ( ' f {} - - - - % S ' ,
xdg_config_home if user else ' /var/lib ' ,
user = user )
xdg_cache_home = os . getenv ( ' XDG_CACHE_HOME ' )
if xdg_cache_home is not None or not user :
test_content ( ' f {} - - - - % C ' ,
xdg_cache_home if user else ' /var/cache ' ,
user = user )
if xdg_config_home is not None or not user :
test_content ( ' f {} - - - - % L ' ,
xdg_config_home + ' /log ' if user else ' /var/log ' ,
user = user )
test_content ( ' f {} - - - - %% ' , ' % ' , user = user )
2017-11-22 16:13:32 +03:00
2021-03-30 17:37:11 +03:00
def mkfifo ( parent , subpath ) :
os . makedirs ( parent , mode = 0o755 , exist_ok = True )
first_component = subpath . split ( ' / ' ) [ 1 ]
path = parent + ' / ' + first_component
print ( ' path: {} ' . format ( path ) )
os . mkfifo ( path )
def mkdir ( parent , subpath ) :
first_component = subpath . split ( ' / ' ) [ 1 ]
path = parent + ' / ' + first_component
os . makedirs ( path , mode = 0o755 , exist_ok = True )
os . symlink ( path , path + ' /self ' , target_is_directory = True )
def symlink ( parent , subpath ) :
link_path = parent + ' /link-target '
os . makedirs ( parent , mode = 0o755 , exist_ok = True )
with open ( link_path , ' wb ' ) as f :
f . write ( b ' target ' )
first_component = subpath . split ( ' / ' ) [ 1 ]
path = parent + ' / ' + first_component
os . symlink ( link_path , path , target_is_directory = True )
def file ( parent , subpath ) :
content = ' file- ' + subpath . split ( ' / ' ) [ 1 ]
path = parent + subpath
os . makedirs ( os . path . dirname ( path ) , mode = 0o755 , exist_ok = True )
with open ( path , ' wb ' ) as f :
f . write ( content . encode ( ) )
def valid_symlink ( parent , subpath ) :
target = ' link-target '
link_path = parent + target
os . makedirs ( link_path , mode = 0o755 , exist_ok = True )
first_component = subpath . split ( ' / ' ) [ 1 ]
path = parent + ' / ' + first_component
os . symlink ( target , path , target_is_directory = True )
def test_hard_cleanup ( * , user ) :
type_cbs = [ None , file , mkdir , symlink ]
if ' mkfifo ' in dir ( os ) :
type_cbs . append ( mkfifo )
for type_cb in type_cbs :
for subpath in [ ' /shallow ' , ' /deep/1/2 ' ] :
label = ' {} - {} ' . format ( ' None ' if type_cb is None else type_cb . __name__ , subpath . split ( ' / ' ) [ 1 ] )
test_content ( ' f= {} - - - - ' + label , label , user = user , subpath = subpath , path_cb = type_cb )
# Test the case that a valid symlink is in the path.
label = ' valid_symlink-deep '
test_content ( ' f= {} - - - - ' + label , label , user = user , subpath = ' /deep/1/2 ' , path_cb = valid_symlink )
2017-11-22 16:13:32 +03:00
if __name__ == ' __main__ ' :
2017-11-23 15:56:32 +03:00
test_invalids ( user = False )
test_invalids ( user = True )
2018-06-03 19:57:21 +03:00
test_uninitialized_t ( )
2017-12-01 23:15:51 +03:00
test_valid_specifiers ( user = False )
test_valid_specifiers ( user = True )
2021-03-30 17:37:11 +03:00
test_hard_cleanup ( user = False )
test_hard_cleanup ( user = True )