曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
#!/usr/bin/python
import os
import subprocess
CAGEFS_MP_FILENAME = '/etc/cagefs/cagefs.mp'
CAGEFSCTL_TOOL = '/usr/sbin/cagefsctl'
class CagefsMpConflict(Exception):
def __init__(self, new_item, existing_item):
self._msg = "Conflict in adding '%s' to %s because of pre-existing alternative specification: '%s'" \
% (new_item, CAGEFS_MP_FILENAME, existing_item)
def __str__(self):
return self._msg
class CagefsMpItem(object):
_PREFIX_LIST = '@!%';
_PREFIX_MOUNT_RW = '';
_PREFIX_MOUNT_RO = '!';
def __init__(self, arg):
"""Constructor
:param arg: Is either path to add to cagefs.mp or a raw line is read from cagefs.mp
:param prefix: The same as adding prefix '!' to arg before passing it to ctor"""
if arg[:1] == '#': # is a comment? then init as dummy
self._path_spec = None
elif arg.strip() == '': # init as dummy for empty lines
self._path_spec = None
else:
self._path_spec = arg
def mode(self, mode):
"Specify mode as in fluent constructor"
if self.prefix() == '@' and not mode is None:
self._path_spec = "%s,%03o" % (self._path_spec, mode)
return self
def __str__(self): return self._path_spec
def _add_slash(path):
if path == '':
return '/'
if (path[-1] != '/'):
return path + '/'
return path
_add_slash = staticmethod(_add_slash)
def pre_exist_in(self, another):
adopted = CagefsMpItem._adopt(another)
# overkill: just to keep strictly to comparing NULL objects principle
if self.is_dummy() or adopted.is_dummy():
return False
this_path = CagefsMpItem._add_slash(self.path())
test_preexist_in_path = CagefsMpItem._add_slash(adopted.path())
return this_path.startswith(test_preexist_in_path)
def is_compatible_by_prefix_with(self, existing):
adopted = CagefsMpItem._adopt(existing)
# overkill: just to keep strictly to comparing NULL objects principle
if self.is_dummy() or adopted.is_dummy():
return False
if self.prefix() == adopted.prefix():
return True
prefix_compatibility_map = { CagefsMpItem._PREFIX_MOUNT_RW : [CagefsMpItem._PREFIX_MOUNT_RO] }
null_options = []
return self.prefix() in prefix_compatibility_map.get(adopted.prefix(), null_options)
def is_dummy(self): return self._path_spec is None
def _adopt(x):
if isinstance(x, CagefsMpItem):
return x
else:
return CagefsMpItem(x)
_adopt = staticmethod(_adopt)
def _cut_off_mode(path_spec):
"""Cut off mode from path spec like @/var/run/screen,777
Only one comma per path spec is allowed ;-)"""
return path_spec.split(',')[0]
_cut_off_mode = staticmethod(_cut_off_mode)
def _cut_off_prefix(path_spec): return path_spec.lstrip(CagefsMpItem._PREFIX_LIST)
_cut_off_prefix = staticmethod(_cut_off_prefix)
def path(self):
return CagefsMpItem._cut_off_prefix(CagefsMpItem._cut_off_mode(self._path_spec))
def prefix(self):
if self._path_spec != self.path():
return self._path_spec[0]
else:
return ''
def is_cagefs_present():
return os.path.exists(CAGEFS_MP_FILENAME) and \
os.path.exists(CAGEFSCTL_TOOL)
def _mk_mount_dir_setup_perm(path, mode=0755, owner_id=None, group_id=None):
if not os.path.isdir(path):
os.mkdir(path)
if not mode is None:
os.chmod(path, mode)
if (not owner_id is None) and (not group_id is None):
os.chown(path, owner_id, group_id)
def setup_mount_dir_cagefs(path, added_by, mode=0755, owner_id=None, group_id=None, prefix=''):
'''Add mount point to /etc/cagefs/cagefs.mp
:param path: Directory path to be added in cagefs.mp and mounted
from within setup_mount_dir_cagefs().
If this directory does not exist, then it is created.
:param added_by: package or component, mount dir relates to, or whatever will
stay in cagefs.mp with "# added by..." comment
:param mode: If is not None: Regardless of whether directory exists or not prior this call,
it's permissions will be set to mode.
:param owner_id: If group_id is also provided along:
Regardless of whether directory exists or not prior this call,
it's owner id will be set to.
:param group_id: If owner_id is also provided along:
Regardless of whether directory exists or not prior this call,
it's group id will be set to.
:param prefix: Mount point prefix. Default is mount as RW.
Pass '!' to add read-only mount point.
Refer CageFS section at http://docs.cloudlinux.com/ for more options.
:returns: None
Propagates native EnvironmentError if no CageFS installed or something else goes wrong.
Raises CagefsMpConflict if path is already specified in cagefs.mp, but in a way which is opposite
to mount_as_readonly param.
'''
_mk_mount_dir_setup_perm(path, mode, owner_id, owner_id)
subprocess.call([CAGEFSCTL_TOOL, "--check-mp"])
# ^^
# Hereafter we will not care if there was
# 'no newline at the end of file'
cagefs_mp = open(CAGEFS_MP_FILENAME, "r+")
try:
new_item = CagefsMpItem(prefix + path).mode(mode)
trim_nl_iter = (file_line.rstrip() for file_line in cagefs_mp)
pre_exist_option = filter(lambda x: new_item.pre_exist_in(x), trim_nl_iter)
if not pre_exist_option:
cagefs_mp.seek(0, 2) # 2: seek to the end of file
# no newline is allowed
added_by = added_by.replace("\n", " ")
print >>cagefs_mp, "# next line is added by", added_by
print >>cagefs_mp, new_item
cagefs_mp.close()
subprocess.call([CAGEFSCTL_TOOL, "--remount-all"])
elif not new_item.is_compatible_by_prefix_with(pre_exist_option[-1]):
raise CagefsMpConflict(new_item, pre_exist_option[-1])
finally:
cagefs_mp.close()