Related
I'm writing a windows service in Python, which at some point needs to lock windows if a specific condition happens (for example the person leaves the pc un-attended for some time).
The solution I found was to use user32.LockWorkStation() using the ctypes module.
ctypes.windll.user32.LockWorkStation()
However, after failing at locking the OS, I noticed the LockWorkstation specifically works in the
interactive [desktop] mode which windows services do not support. it actually wasted quite a bit of my time as it works when debugging! any way,
Quoting from LockWorkStation function:
The LockWorkStation function is callable only by processes running on
the interactive desktop. In addition, the user must be logged on, and
the workstation cannot already be locked.
windows services do have a property where you can allow it to interact with the desktop (on log on tab), however, this feature is if I'm not mistaken, disabled on latest versions of windows and apart from that, is not a good idea to enable it either.
Also quoting from MSDN:
Services do not have message loops, unless they are allowed to
interact with the desktop. If the message loop is not provided by a
hidden form, as in this example, the service must be run under the
local system account, and manual intervention is required to enable
interaction with the desktop. That is, the administrator must manually
check the Allow service to interact with desktop check box on the Log
On tab of the service properties dialog box. In that case, a message
loop is automatically provided. This option is available only when the
service is run under the local system account. Interaction with the
desktop cannot be enabled programmatically.
Therefore I'm looking for other solutions that would allow me to lock the windows from a windows service. How can I achieve this ?
Summary
In order to interact with user session in a service, you first need to use a user session id. Basically you'll need to use WTSGetActiveConsoleSessionId, WTSGetActiveConsoleSessionId and CreateEnvironmentBlock prior to calling CreateProcessAsUser. Here is the snippet that does the trick:
import win32process
import win32con
import win32ts
console_session_id = win32ts.WTSGetActiveConsoleSessionId()
console_user_token = win32ts.WTSQueryUserToken(console_session_id)
startup = win32process.STARTUPINFO()
priority = win32con.NORMAL_PRIORITY_CLASS
environment = win32profile.CreateEnvironmentBlock(console_user_token, False)
handle, thread_id ,pid, tid = win32process.CreateProcessAsUser(console_user_token, None, "rundll32.exe user32.dll,LockWorkStation", None, None, True, priority, environment, None, startup)
If you need to call a specific application you may call this like this:
win32process.CreateProcessAsUser(console_user_token, your_app_exe, app_args, None, None, True, priority, environment, None, startup)
This is actually how services in windows interact with user sessions. Using this method, you no longer need the user credentials.
Long Explanation:
When it comes to Windows services to access/interact with user session (session >0). It's usually recommended to use CreateProcessAsUser(). one would go on and do something like this (ref):
user = "username"
pword = "123456"
domain = "." # means current domain
logontype = win32con.LOGON32_LOGON_INTERACTIVE
# some may suggest to use BATCH mode instead in case you fail! but this doesn't work either!
# logontype = win32con.LOGON32_LOGON_BATCH
provider = win32con.LOGON32_PROVIDER_WINNT50
token = win32security.LogonUser(user, domain, pword, logontype, provider)
startup = win32process.STARTUPINFO()
process_information = PROCESS_INFORMATION()
cwd = os.path.dirname(__file__)
lock_file = os.path.join(cwd,'system_locker.exe')
appname = lock_file
priority = win32con.NORMAL_PRIORITY_CLASS
result = win32process.CreateProcessAsUser(token, appname, None, None, None, True, priority, None, None, startup)
but if you go this way, You'll face the error :
(1314, 'CreateProcessAsUser', 'A required privilege is not held by the client.')
There are many suggestions to get rid of this issue, such as disabling UAC, etc. but none will work and if you look at the MSDN documentation about CreateProcessAsUser, you'll see :
Typically, the process that calls the CreateProcessAsUser function
must have the SE_INCREASE_QUOTA_NAME privilege and may require the
SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable.
If this function fails with ERROR_PRIVILEGE_NOT_HELD (1314), use the
CreateProcessWithLogonW function instead. CreateProcessWithLogonW
requires no special privileges, but the specified user account must be
allowed to log on interactively. Generally, it is best to use
CreateProcessWithLogonW to create a process with alternate
credentials.
which means to use CreateProcessWithLogonW. If one goes and tries this for example like this :
from ctypes import *
from ctypes.wintypes import *
INVALID_HANDLE_VALUE = -1
CREATE_UNICODE_ENVIRONMENT = 0x00000400
CData = Array.__base__
LPBYTE = POINTER(BYTE)
class PROCESS_INFORMATION(Structure):
'''http://msdn.microsoft.com/en-us/library/ms684873'''
_fields_ = [
('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', DWORD),
('dwThreadId', DWORD),
]
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
class STARTUPINFOW(Structure):
'http://msdn.microsoft.com/en-us/library/ms686331'
_fields_ = [
('cb', DWORD),
('lpReserved', LPWSTR),
('lpDesktop', LPWSTR),
('lpTitle', LPWSTR),
('dwX', DWORD),
('dwY', DWORD),
('dwXSize', DWORD),
('dwYSize', DWORD),
('dwXCountChars', DWORD),
('dwYCountChars', DWORD),
('dwFillAttribute', DWORD),
('dwFlags', DWORD),
('wShowWindow', WORD),
('cbReserved2', WORD),
('lpReserved2', LPBYTE),
('hStdInput', HANDLE),
('hStdOutput', HANDLE),
('hStdError', HANDLE),
]
LPSTARTUPINFOW = POINTER(STARTUPINFOW)
# http://msdn.microsoft.com/en-us/library/ms682431
windll.advapi32.CreateProcessWithLogonW.restype = BOOL
windll.advapi32.CreateProcessWithLogonW.argtypes = [
LPCWSTR, # lpUsername
LPCWSTR, # lpDomain
LPCWSTR, # lpPassword
DWORD, # dwLogonFlags
LPCWSTR, # lpApplicationName
LPWSTR, # lpCommandLine (inout)
DWORD, # dwCreationFlags
LPCWSTR, # lpEnvironment (force Unicode)
LPCWSTR, # lpCurrentDirectory
LPSTARTUPINFOW, # lpStartupInfo
LPPROCESS_INFORMATION, # lpProcessInfo (out)
]
def CreateProcessWithLogonW(
lpUsername=None,
lpDomain=None,
lpPassword=None,
dwLogonFlags=0,
lpApplicationName=None,
lpCommandLine=None,
dwCreationFlags=0,
lpEnvironment=None,
lpCurrentDirectory=None,
startupInfo=None
):
if (lpCommandLine is not None and
not isinstance(lpCommandLine, CData)
):
lpCommandLine = create_unicode_buffer(lpCommandLine)
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT
if startupInfo is None:
startupInfo = STARTUPINFOW(sizeof(STARTUPINFOW))
processInformation = PROCESS_INFORMATION(
INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE)
success = windll.advapi32.CreateProcessWithLogonW(
lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName,
lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory,
byref(startupInfo), byref(processInformation))
if not success:
raise WinError()
return processInformation
....
result = CreateProcessWithLogonW(user, domain, pword, 0, None, "rundll32.exe user32.dll,LockWorkStation")
He/she will face the error :
(13, 'Access is denied.', None, 5)
Another similar implementation that fails is as follows (taken from):
from ctypes import wintypes
from subprocess import PIPE
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
ERROR_INVALID_HANDLE = 0x0006
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
INVALID_DWORD_VALUE = wintypes.DWORD(-1).value
DEBUG_PROCESS = 0x00000001
DEBUG_ONLY_THIS_PROCESS = 0x00000002
CREATE_SUSPENDED = 0x00000004
DETACHED_PROCESS = 0x00000008
CREATE_NEW_CONSOLE = 0x00000010
CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_UNICODE_ENVIRONMENT = 0x00000400
CREATE_SEPARATE_WOW_VDM = 0x00000800
CREATE_SHARED_WOW_VDM = 0x00001000
INHERIT_PARENT_AFFINITY = 0x00010000
CREATE_PROTECTED_PROCESS = 0x00040000
EXTENDED_STARTUPINFO_PRESENT = 0x00080000
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000
CREATE_DEFAULT_ERROR_MODE = 0x04000000
CREATE_NO_WINDOW = 0x08000000
STARTF_USESHOWWINDOW = 0x00000001
STARTF_USESIZE = 0x00000002
STARTF_USEPOSITION = 0x00000004
STARTF_USECOUNTCHARS = 0x00000008
STARTF_USEFILLATTRIBUTE = 0x00000010
STARTF_RUNFULLSCREEN = 0x00000020
STARTF_FORCEONFEEDBACK = 0x00000040
STARTF_FORCEOFFFEEDBACK = 0x00000080
STARTF_USESTDHANDLES = 0x00000100
STARTF_USEHOTKEY = 0x00000200
STARTF_TITLEISLINKNAME = 0x00000800
STARTF_TITLEISAPPID = 0x00001000
STARTF_PREVENTPINNING = 0x00002000
SW_HIDE = 0
SW_SHOWNORMAL = 1
SW_SHOWMINIMIZED = 2
SW_SHOWMAXIMIZED = 3
SW_SHOWNOACTIVATE = 4
SW_SHOW = 5
SW_MINIMIZE = 6
SW_SHOWMINNOACTIVE = 7
SW_SHOWNA = 8
SW_RESTORE = 9
SW_SHOWDEFAULT = 10 # ~STARTUPINFO
SW_FORCEMINIMIZE = 11
LOGON_WITH_PROFILE = 0x00000001
LOGON_NETCREDENTIALS_ONLY = 0x00000002
STD_INPUT_HANDLE = wintypes.DWORD(-10).value
STD_OUTPUT_HANDLE = wintypes.DWORD(-11).value
STD_ERROR_HANDLE = wintypes.DWORD(-12).value
class HANDLE(wintypes.HANDLE):
__slots__ = 'closed',
def __int__(self):
return self.value or 0
def Detach(self):
if not getattr(self, 'closed', False):
self.closed = True
value = int(self)
self.value = None
return value
raise ValueError("already closed")
def Close(self, CloseHandle=kernel32.CloseHandle):
if self and not getattr(self, 'closed', False):
CloseHandle(self.Detach())
__del__ = Close
def __repr__(self):
return "%s(%d)" % (self.__class__.__name__, int(self))
class PROCESS_INFORMATION(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms684873"""
__slots__ = '_cached_hProcess', '_cached_hThread'
_fields_ = (('_hProcess', HANDLE),
('_hThread', HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD))
#property
def hProcess(self):
if not hasattr(self, '_cached_hProcess'):
self._cached_hProcess = self._hProcess
return self._cached_hProcess
#property
def hThread(self):
if not hasattr(self, '_cached_hThread'):
self._cached_hThread = self._hThread
return self._cached_hThread
def __del__(self):
try:
self.hProcess.Close()
finally:
self.hThread.Close()
LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
LPBYTE = ctypes.POINTER(wintypes.BYTE)
class STARTUPINFO(ctypes.Structure):
"""https://msdn.microsoft.com/en-us/library/ms686331"""
_fields_ = (('cb', wintypes.DWORD),
('lpReserved', wintypes.LPWSTR),
('lpDesktop', wintypes.LPWSTR),
('lpTitle', wintypes.LPWSTR),
('dwX', wintypes.DWORD),
('dwY', wintypes.DWORD),
('dwXSize', wintypes.DWORD),
('dwYSize', wintypes.DWORD),
('dwXCountChars', wintypes.DWORD),
('dwYCountChars', wintypes.DWORD),
('dwFillAttribute', wintypes.DWORD),
('dwFlags', wintypes.DWORD),
('wShowWindow', wintypes.WORD),
('cbReserved2', wintypes.WORD),
('lpReserved2', LPBYTE),
('hStdInput', wintypes.HANDLE),
('hStdOutput', wintypes.HANDLE),
('hStdError', wintypes.HANDLE))
def __init__(self, **kwds):
self.cb = ctypes.sizeof(self)
super(STARTUPINFO, self).__init__(**kwds)
class PROC_THREAD_ATTRIBUTE_LIST(ctypes.Structure):
pass
PPROC_THREAD_ATTRIBUTE_LIST = ctypes.POINTER(PROC_THREAD_ATTRIBUTE_LIST)
class STARTUPINFOEX(STARTUPINFO):
_fields_ = (('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),)
LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO)
LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX)
class SECURITY_ATTRIBUTES(ctypes.Structure):
_fields_ = (('nLength', wintypes.DWORD),
('lpSecurityDescriptor', wintypes.LPVOID),
('bInheritHandle', wintypes.BOOL))
def __init__(self, **kwds):
self.nLength = ctypes.sizeof(self)
super(SECURITY_ATTRIBUTES, self).__init__(**kwds)
LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES)
class HANDLE_IHV(HANDLE):
pass
class DWORD_IDV(wintypes.DWORD):
pass
def _check_ihv(result, func, args):
if result.value == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
return result.value
def _check_idv(result, func, args):
if result.value == INVALID_DWORD_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
return result.value
def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
def WIN(func, restype, *argtypes):
func.restype = restype
func.argtypes = argtypes
if issubclass(restype, HANDLE_IHV):
func.errcheck = _check_ihv
elif issubclass(restype, DWORD_IDV):
func.errcheck = _check_idv
else:
func.errcheck = _check_bool
# https://msdn.microsoft.com/en-us/library/ms724211
WIN(kernel32.CloseHandle, wintypes.BOOL,
wintypes.HANDLE,) # _In_ HANDLE hObject
# https://msdn.microsoft.com/en-us/library/ms685086
WIN(kernel32.ResumeThread, DWORD_IDV,
wintypes.HANDLE,) # _In_ hThread
# https://msdn.microsoft.com/en-us/library/ms682425
WIN(kernel32.CreateProcessW, wintypes.BOOL,
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
LPSECURITY_ATTRIBUTES, # _In_opt_ lpProcessAttributes
LPSECURITY_ATTRIBUTES, # _In_opt_ lpThreadAttributes
wintypes.BOOL, # _In_ bInheritHandles
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
# https://msdn.microsoft.com/en-us/library/ms682429
WIN(advapi32.CreateProcessAsUserW, wintypes.BOOL,
wintypes.HANDLE, # _In_opt_ hToken
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
LPSECURITY_ATTRIBUTES, # _In_opt_ lpProcessAttributes
LPSECURITY_ATTRIBUTES, # _In_opt_ lpThreadAttributes
wintypes.BOOL, # _In_ bInheritHandles
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
# https://msdn.microsoft.com/en-us/library/ms682434
WIN(advapi32.CreateProcessWithTokenW, wintypes.BOOL,
wintypes.HANDLE, # _In_ hToken
wintypes.DWORD, # _In_ dwLogonFlags
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
# https://msdn.microsoft.com/en-us/library/ms682431
WIN(advapi32.CreateProcessWithLogonW, wintypes.BOOL,
wintypes.LPCWSTR, # _In_ lpUsername
wintypes.LPCWSTR, # _In_opt_ lpDomain
wintypes.LPCWSTR, # _In_ lpPassword
wintypes.DWORD, # _In_ dwLogonFlags
wintypes.LPCWSTR, # _In_opt_ lpApplicationName
wintypes.LPWSTR, # _Inout_opt_ lpCommandLine
wintypes.DWORD, # _In_ dwCreationFlags
wintypes.LPCWSTR, # _In_opt_ lpEnvironment
wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory
LPSTARTUPINFO, # _In_ lpStartupInfo
LPPROCESS_INFORMATION) # _Out_ lpProcessInformation
CREATION_TYPE_NORMAL = 0
CREATION_TYPE_LOGON = 1
CREATION_TYPE_TOKEN = 2
CREATION_TYPE_USER = 3
class CREATIONINFO(object):
__slots__ = ('dwCreationType',
'lpApplicationName', 'lpCommandLine', 'bUseShell',
'lpProcessAttributes', 'lpThreadAttributes', 'bInheritHandles',
'dwCreationFlags', 'lpEnvironment', 'lpCurrentDirectory',
'hToken', 'lpUsername', 'lpDomain', 'lpPassword', 'dwLogonFlags')
def __init__(self, dwCreationType=CREATION_TYPE_NORMAL,
lpApplicationName=None, lpCommandLine=None, bUseShell=False,
lpProcessAttributes=None, lpThreadAttributes=None,
bInheritHandles=False, dwCreationFlags=0, lpEnvironment=None,
lpCurrentDirectory=None, hToken=None, dwLogonFlags=0,
lpUsername=None, lpDomain=None, lpPassword=None):
self.dwCreationType = dwCreationType
self.lpApplicationName = lpApplicationName
self.lpCommandLine = lpCommandLine
self.bUseShell = bUseShell
self.lpProcessAttributes = lpProcessAttributes
self.lpThreadAttributes = lpThreadAttributes
self.bInheritHandles = bInheritHandles
self.dwCreationFlags = dwCreationFlags
self.lpEnvironment = lpEnvironment
self.lpCurrentDirectory = lpCurrentDirectory
self.hToken = hToken
self.lpUsername = lpUsername
self.lpDomain = lpDomain
self.lpPassword = lpPassword
self.dwLogonFlags = dwLogonFlags
def create_environment(environ):
if environ is not None:
items = ['%s=%s' % (k, environ[k]) for k in sorted(environ)]
buf = '\x00'.join(items)
length = len(buf) + 2 if buf else 1
return ctypes.create_unicode_buffer(buf, length)
def create_process(commandline=None, creationinfo=None, startupinfo=None):
if creationinfo is None:
creationinfo = CREATIONINFO()
if startupinfo is None:
startupinfo = STARTUPINFO()
elif isinstance(startupinfo, subprocess.STARTUPINFO):
startupinfo = STARTUPINFO(dwFlags=startupinfo.dwFlags,
hStdInput=startupinfo.hStdInput,
hStdOutput=startupinfo.hStdOutput,
hStdError=startupinfo.hStdError,
wShowWindow=startupinfo.wShowWindow)
si, ci, pi = startupinfo, creationinfo, PROCESS_INFORMATION()
if commandline is None:
commandline = ci.lpCommandLine
if commandline is not None:
if ci.bUseShell:
si.dwFlags |= STARTF_USESHOWWINDOW
si.wShowWindow = SW_HIDE
comspec = os.environ.get("ComSpec", os.path.join(
os.environ["SystemRoot"], "System32", "cmd.exe"))
commandline = '"{}" /c "{}"'.format(comspec, commandline)
commandline = ctypes.create_unicode_buffer(commandline)
dwCreationFlags = ci.dwCreationFlags | CREATE_UNICODE_ENVIRONMENT
lpEnvironment = create_environment(ci.lpEnvironment)
if (dwCreationFlags & DETACHED_PROCESS and
((dwCreationFlags & CREATE_NEW_CONSOLE) or
(ci.dwCreationType == CREATION_TYPE_LOGON) or
(ci.dwCreationType == CREATION_TYPE_TOKEN))):
raise RuntimeError('DETACHED_PROCESS is incompatible with '
'CREATE_NEW_CONSOLE, which is implied for '
'the logon and token creation types')
if ci.dwCreationType == CREATION_TYPE_NORMAL:
kernel32.CreateProcessW(
ci.lpApplicationName, commandline,
ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
elif ci.dwCreationType == CREATION_TYPE_LOGON:
advapi32.CreateProcessWithLogonW(
ci.lpUsername, ci.lpDomain, ci.lpPassword, ci.dwLogonFlags,
ci.lpApplicationName, commandline,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
elif ci.dwCreationType == CREATION_TYPE_TOKEN:
advapi32.CreateProcessWithTokenW(
ci.hToken, ci.dwLogonFlags,
ci.lpApplicationName, commandline,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
elif ci.dwCreationType == CREATION_TYPE_USER:
advapi32.CreateProcessAsUserW(
ci.hToken,
ci.lpApplicationName, commandline,
ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
ctypes.byref(si), ctypes.byref(pi))
else:
raise ValueError('invalid process creation type')
return pi
class Popen(subprocess.Popen):
def __init__(self, *args, **kwds):
ci = self._creationinfo = kwds.pop('creationinfo', CREATIONINFO())
if kwds.pop('suspended', False):
ci.dwCreationFlags |= CREATE_SUSPENDED
self._child_started = False
super(Popen, self).__init__(*args, **kwds)
if sys.version_info[0] == 2:
def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines, startupinfo,
creationflags, shell, to_close, p2cread, p2cwrite,
c2pread, c2pwrite, errread, errwrite):
"""Execute program (MS Windows version)"""
commandline = (args if isinstance(args, types.StringTypes) else
subprocess.list2cmdline(args))
self._common_execute_child(executable, commandline, shell,
close_fds, creationflags, env, cwd,
startupinfo, p2cread, c2pwrite, errwrite, to_close)
else:
def _execute_child(self, args, executable, preexec_fn, close_fds,
pass_fds, cwd, env, startupinfo, creationflags,
shell, p2cread, p2cwrite, c2pread, c2pwrite, errread,
errwrite, restore_signals, start_new_session):
"""Execute program (MS Windows version)"""
assert not pass_fds, "pass_fds not supported on Windows."
commandline = (args if isinstance(args, str) else
subprocess.list2cmdline(args))
self._common_execute_child(executable, commandline, shell,
close_fds, creationflags, env, cwd,
startupinfo, p2cread, c2pwrite, errwrite)
def _common_execute_child(self, executable, commandline, shell,
close_fds, creationflags, env, cwd,
startupinfo, p2cread, c2pwrite, errwrite,
to_close=()):
ci = self._creationinfo
if executable is not None:
ci.lpApplicationName = executable
if commandline:
ci.lpCommandLine = commandline
if shell:
ci.bUseShell = shell
if not close_fds:
ci.bInheritHandles = int(not close_fds)
if creationflags:
ci.dwCreationFlags |= creationflags
if env is not None:
ci.lpEnvironment = env
if cwd is not None:
ci.lpCurrentDirectory = cwd
if startupinfo is None:
startupinfo = STARTUPINFO()
si = self._startupinfo = startupinfo
default = None if sys.version_info[0] == 2 else -1
if default not in (p2cread, c2pwrite, errwrite):
si.dwFlags |= STARTF_USESTDHANDLES
si.hStdInput = int( p2cread)
si.hStdOutput = int(c2pwrite)
si.hStdError = int(errwrite)
try:
pi = create_process(creationinfo=ci, startupinfo=si)
finally:
if sys.version_info[0] == 2:
if p2cread is not None:
p2cread.Close()
to_close.remove(p2cread)
if c2pwrite is not None:
c2pwrite.Close()
to_close.remove(c2pwrite)
if errwrite is not None:
errwrite.Close()
to_close.remove(errwrite)
else:
if p2cread != -1:
p2cread.Close()
if c2pwrite != -1:
c2pwrite.Close()
if errwrite != -1:
errwrite.Close()
if hasattr(self, '_devnull'):
os.close(self._devnull)
if not ci.dwCreationFlags & CREATE_SUSPENDED:
self._child_started = True
# Retain the process handle, but close the thread handle
# if it's no longer needed.
self._processinfo = pi
self._handle = pi.hProcess.Detach()
self.pid = pi.dwProcessId
if self._child_started:
pi.hThread.Close()
def start(self):
if self._child_started:
raise RuntimeError("processes can only be started once")
hThread = self._processinfo.hThread
prev_count = kernel32.ResumeThread(hThread)
if prev_count > 1:
for i in range(1, prev_count):
if kernel32.ResumeThread(hThread) <= 1:
break
else:
raise RuntimeError('cannot start the main thread')
# The thread's previous suspend count was 0 or 1,
# so it should be running now.
self._child_started = True
hThread.Close()
def __del__(self):
if not self._child_started:
try:
if hasattr(self, '_processinfo'):
self._processinfo.hThread.Close()
finally:
if hasattr(self, '_handle'):
self.terminate()
super(Popen, self).__del__()
....
cmd = "rundll32.exe user32.dll,LockWorkStation" #lock_file
ci = CREATIONINFO(CREATION_TYPE_LOGON,
lpUsername=user,
lpPassword=pword)
p = Popen(cmd, suspended=True, creationinfo=ci,
stdout=PIPE, universal_newlines=True)
p.start()
fails with the same error.
And then we reach to the final solution that actually works which is to use use WTSGetActiveConsoleSessionId, WTSGetActiveConsoleSessionId and CreateEnvironmentBlock prior to calling CreateProcessAsUser. The most important part imho, is the CreateEnvironmentBlock which is essential for this to work. The first two methods allow us not to use a predefined user/pass.
Useful links :
#EugeneMayevski'Callback provided two links that discusses about this as well:
1. calling-createprocessasuser-from-service
2. calling-createprocessasuser-from-a-user-process-launched-from-a-service
Important Note :
In debug mode when using the snippet I provided, you may face the 1314 error for WTSGetActiveConsoleSessionId its spected and the live service wont face this error and will run just fine.
The problem is not with services being able or unable to interact with desktop, but with them being run in a separate Windows Session. Besides explaining your problem a lot, this article also suggests a possible solution:
For more complex UI, use the CreateProcessAsUser function to create a process in the user's session.
You can even re-run python with your own script but in the user's session.
The difficulty here would be to determine, which session you should lock and which user you should impersonate for this. There can really be several users, logged in concurrently even locally. But most likely (if you are creating a service for yourself), you can simply impersonate the user on session 1.
Good days, I'm new to python and trying to run a demo provided by Invensense.(9-axis MPU9250 connects a STM32F407G discovery board, I used code and python client in motion_driver_6.12 which downloaded from Invensense website.)
the whole python part are python2.7, pysearil, pygame.
I searched my issues in Stackoverflow, but the specific situations are a little different, and most of the solutions are useless for me.
First, I show my issues.
UART connects the PC, run Invensense's python client through cmd.exe, pygame window appears briefly and disappear and I get the following error
D:\motion_driver_6.12\eMPL-pythonclient>python eMPL-client.py 7
Traceback (most recent call last):
File "eMPL-client.py", line 273, in <module>
def four_bytes(d1, d2, d3, d4):
File "eMPL-client.py", line 12, in __init__
File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 31, in
__init__
super(Serial, self).__init__(*args, **kwargs)
File "C:\Python27\lib\site-packages\serial\serialutil.py", line 218, in
__init__
self.port = port
File "C:\Python27\lib\site-packages\serial\serialutil.py", line 264, in port
raise ValueError('"port" must be None or a string, not
{}'.format(type(port)))
ValueError: "port" must be None or a string, not <type 'int'>
Second, through the similar questions, what I have done until now:
open the file "serialwin32.py" .Change port = self.name to port = str(self.name). It doesn't work, same error messages.
uninstall the pyserial3.3(the lastest version), using a pyserial2.7. The error meesages were gone but Pygmae now just sits there with a black screen.The old answer said "Invensense tells me that means it is connected and waiting for data".
----------------followed is eMPL-client.py, line 21 and line 273 are marked-----
#!/usr/bin/python
# eMPL_client.py
# A PC application for use with Embedded MotionApps.
# Copyright 2012 InvenSense, Inc. All Rights Reserved.
import serial, sys, time, string, pygame
from ponycube import *
class eMPL_packet_reader:
//*********************line 21 __init__ begins********************//
def __init__(self, port, quat_delegate=None, debug_delegate=None, data_delegate=None ):
self.s = serial.Serial(port,115200)
self.s.setTimeout(0.1)
self.s.setWriteTimeout(0.2)
# TODO: Will this break anything?
##Client attempts to write to eMPL.
#try:
#self.s.write("\n")
#except serial.serialutil.SerialTimeoutException:
#pass # write will timeout if umpl app is already started.
if quat_delegate:
self.quat_delegate = quat_delegate
else:
self.quat_delegate = empty_packet_delegate()
if debug_delegate:
self.debug_delegate = debug_delegate
else:
self.debug_delegate = empty_packet_delegate()
if data_delegate:
self.data_delegate = data_delegate
else:
self.data_delegate = empty_packet_delegate()
self.packets = []
self.length = 0
self.previous = None
def read(self):
NUM_BYTES = 23
p = None
while self.s.inWaiting() >= NUM_BYTES:
rs = self.s.read(NUM_BYTES)
if ord(rs[0]) == ord('$'):
pkt_code = ord(rs[1])
if pkt_code == 1:
d = debug_packet(rs)
self.debug_delegate.dispatch(d)
elif pkt_code == 2:
p = quat_packet(rs)
self.quat_delegate.dispatch(p)
elif pkt_code == 3:
d = data_packet(rs)
self.data_delegate.dispatch(d)
else:
print "no handler for pkt_code",pkt_code
else:
c = ' '
print "serial misaligned!"
while not ord(c) == ord('$'):
c = self.s.read(1)
self.s.read(NUM_BYTES-1)
def write(self,a):
self.s.write(a)
def close(self):
self.s.close()
def write_log(self,fname):
f = open(fname,'w')
for p in self.packets:
f.write(p.logfile_line())
f.close()
# =========== PACKET DELEGATES ==========
class packet_delegate(object):
def loop(self,event):
print "generic packet_delegate loop w/event",event
def dispatch(self,p):
print "generic packet_delegate dispatched",p
class empty_packet_delegate(packet_delegate):
def loop(self,event):
pass
def dispatch(self,p):
pass
class cube_packet_viewer (packet_delegate):
def __init__(self):
self.screen = Screen(480,400,scale=1.5)
self.cube = Cube(30,60,10)
self.q = Quaternion(1,0,0,0)
self.previous = None # previous quaternion
self.latest = None # latest packet (get in dispatch, use in loop)
def loop(self,event):
packet = self.latest
if packet:
q = packet.to_q().normalized()
self.cube.erase(self.screen)
self.cube.draw(self.screen,q)
pygame.display.flip()
self.latest = None
def dispatch(self,p):
if isinstance(p,quat_packet):
self.latest = p
class debug_packet_viewer (packet_delegate):
def loop(self,event):
pass
def dispatch(self,p):
assert isinstance(p,debug_packet);
p.display()
class data_packet_viewer (packet_delegate):
def loop(self,event):
pass
def dispatch(self,p):
assert isinstance(p,data_packet);
p.display()
# =============== PACKETS =================
# For 16-bit signed integers.
def two_bytes(d1,d2):
d = ord(d1)*256 + ord(d2)
if d > 32767:
d -= 65536
return d
# For 32-bit signed integers.
//**************************273 begins*********************************//
def four_bytes(d1, d2, d3, d4):
d = ord(d1)*(1<<24) + ord(d2)*(1<<16) + ord(d3)*(1<<8) + ord(d4)
if d > 2147483648:
d-= 4294967296
return d
----------------followed is serialutil.py(version 3.3) from line1 to line272, 218 and 264 are marked-------------
#! python
#
# Base class and support functions used by various backends.
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# (C) 2001-2016 Chris Liechti <cliechti#gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import io
import time
# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)``
# isn't returning the contents (very unfortunate). Therefore we need special
# cases and test for it. Ensure that there is a ``memoryview`` object for older
# Python versions. This is easier than making every test dependent on its
# existence.
try:
memoryview
except (NameError, AttributeError):
# implementation does not matter as we do not really use it.
# it just must not inherit from something else we might care for.
class memoryview(object): # pylint: disable=redefined-builtin,invalid-name
pass
try:
unicode
except (NameError, AttributeError):
unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name
try:
basestring
except (NameError, AttributeError):
basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name
# "for byte in data" fails for python3 as it returns ints instead of bytes
def iterbytes(b):
"""Iterate over bytes, returning bytes instead of ints (python3)"""
if isinstance(b, memoryview):
b = b.tobytes()
i = 0
while True:
a = b[i:i + 1]
i += 1
if a:
yield a
else:
break
# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11'
# so a simple ``bytes(sequence)`` doesn't work for all versions
def to_bytes(seq):
"""convert a sequence to a bytes type"""
if isinstance(seq, bytes):
return seq
elif isinstance(seq, bytearray):
return bytes(seq)
elif isinstance(seq, memoryview):
return seq.tobytes()
elif isinstance(seq, unicode):
raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq))
else:
# handle list of integers and bytes (one or more items) for Python 2 and 3
return bytes(bytearray(seq))
# create control bytes
XON = to_bytes([17])
XOFF = to_bytes([19])
CR = to_bytes([13])
LF = to_bytes([10])
PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
PARITY_NAMES = {
PARITY_NONE: 'None',
PARITY_EVEN: 'Even',
PARITY_ODD: 'Odd',
PARITY_MARK: 'Mark',
PARITY_SPACE: 'Space',
}
class SerialException(IOError):
"""Base class for serial port related exceptions."""
class SerialTimeoutException(SerialException):
"""Write timeouts give an exception"""
writeTimeoutError = SerialTimeoutException('Write timeout')
portNotOpenError = SerialException('Attempting to use a port that is not open')
class Timeout(object):
"""\
Abstraction for timeout operations. Using time.monotonic() if available
or time.time() in all other cases.
The class can also be initialized with 0 or None, in order to support
non-blocking and fully blocking I/O operations. The attributes
is_non_blocking and is_infinite are set accordingly.
"""
if hasattr(time, 'monotonic'):
# Timeout implementation with time.monotonic(). This function is only
# supported by Python 3.3 and above. It returns a time in seconds
# (float) just as time.time(), but is not affected by system clock
# adjustments.
TIME = time.monotonic
else:
# Timeout implementation with time.time(). This is compatible with all
# Python versions but has issues if the clock is adjusted while the
# timeout is running.
TIME = time.time
def __init__(self, duration):
"""Initialize a timeout with given duration"""
self.is_infinite = (duration is None)
self.is_non_blocking = (duration == 0)
self.duration = duration
if duration is not None:
self.target_time = self.TIME() + duration
else:
self.target_time = None
def expired(self):
"""Return a boolean, telling if the timeout has expired"""
return self.target_time is not None and self.time_left() <= 0
def time_left(self):
"""Return how many seconds are left until the timeout expires"""
if self.is_non_blocking:
return 0
elif self.is_infinite:
return None
else:
delta = self.target_time - self.TIME()
if delta > self.duration:
# clock jumped, recalculate
self.target_time = self.TIME() + self.duration
return self.duration
else:
return max(0, delta)
def restart(self, duration):
"""\
Restart a timeout, only supported if a timeout was already set up
before.
"""
self.duration = duration
self.target_time = self.TIME() + duration
class SerialBase(io.RawIOBase):
"""\
Serial port base class. Provides __init__ function and properties to
get/set port settings.
"""
# default values, may be overridden in subclasses that do not support all values
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
3000000, 3500000, 4000000)
BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE)
STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
def __init__(self,
port=None,
baudrate=9600,
bytesize=EIGHTBITS,
parity=PARITY_NONE,
stopbits=STOPBITS_ONE,
timeout=None,
xonxoff=False,
rtscts=False,
write_timeout=None,
dsrdtr=False,
inter_byte_timeout=None,
exclusive=None,
**kwargs):
"""\
Initialize comm port object. If a "port" is given, then the port will be
opened immediately. Otherwise a Serial port object in closed state
is returned.
"""
self.is_open = False
self.portstr = None
self.name = None
# correct values are assigned below through properties
self._port = None
self._baudrate = None
self._bytesize = None
self._parity = None
self._stopbits = None
self._timeout = None
self._write_timeout = None
self._xonxoff = None
self._rtscts = None
self._dsrdtr = None
self._inter_byte_timeout = None
self._rs485_mode = None # disabled by default
self._rts_state = True
self._dtr_state = True
self._break_state = False
self._exclusive = None
# assign values using get/set methods using the properties feature
//**************218**************//
self.port = port
//**************218**************//
self.baudrate = baudrate
self.bytesize = bytesize
self.parity = parity
self.stopbits = stopbits
self.timeout = timeout
self.write_timeout = write_timeout
self.xonxoff = xonxoff
self.rtscts = rtscts
self.dsrdtr = dsrdtr
self.inter_byte_timeout = inter_byte_timeout
self.exclusive = exclusive
# watch for backward compatible kwargs
if 'writeTimeout' in kwargs:
self.write_timeout = kwargs.pop('writeTimeout')
if 'interCharTimeout' in kwargs:
self.inter_byte_timeout = kwargs.pop('interCharTimeout')
if kwargs:
raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs))
if port is not None:
self.open()
# - - - - - - - - - - - - - - - - - - - - - - - -
# to be implemented by subclasses:
# def open(self):
# def close(self):
# - - - - - - - - - - - - - - - - - - - - - - - -
#property
def port(self):
"""\
Get the current port setting. The value that was passed on init or using
setPort() is passed back.
"""
return self._port
#port.setter
def port(self, port):
"""\
Change the port.
"""
//*************************line 263**********************//
if port is not None and not isinstance(port, basestring):
raise ValueError('"port" must be None or a string, not {}'.format(type(port)))
was_open = self.is_open
if was_open:
self.close()
self.portstr = port
self._port = port
self.name = self.portstr
if was_open:
self.open()
-------------followed is serialwin32.py(version 3.3), 31 is marked-----------------------------------------
#! python
#
# backend for Windows ("win32" incl. 32/64 bit support)
#
# (C) 2001-2015 Chris Liechti <cliechti#gmx.net>
#
# This file is part of pySerial. https://github.com/pyserial/pyserial
# SPDX-License-Identifier: BSD-3-Clause
#
# Initial patch to use ctypes by Giovanni Bajo <rasky#develer.com>
# pylint: disable=invalid-name,too-few-public-methods
import ctypes
import time
from serial import win32
import serial
from serial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError
class Serial(SerialBase):
"""Serial port implementation for Win32 based on ctypes."""
BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200)
def __init__(self, *args, **kwargs):
self._port_handle = None
self._overlapped_read = None
self._overlapped_write = None
//**************31**************//
super(Serial, self).__init__(*args, **kwargs)
//**************31**************//
Questions:
Any ideas how to solve the error?
Anyone who has used the MPU9250 with STM32F407G discovery board? Any suggestion?
two related issues on Stackoverflow
issue one: visit pyserial serialwin32.py has attribute error
issue two: visit Invensense Motion Driver 6.12 STM32 demo python don't work
Looking online, I found this Github repo that appears to correspond to the code you are working with. It appears eMPL-client.py is incompatible with newer versions of pyserial. Specifically, the __main__ routine requires numeric port identifiers, but the pyserial 3.3 serial.Serial requires textual port identifiers. I do not have the setup to test this, but you can try the following.
Install a fresh copy of Python 2.7, which is what eMPL-client.py targets. This is unrelated to pyserial 2.7.
In the fresh copy, install pyserial 2.7 and the other dependencies. Per the source, pyserial 2.7 uses numbers for ports. Pyserial 3.3 uses names for ports, whence the "port must be a string" error.
That should get you past the initial error, which is similar to this answer to the question you linked. At that point, it's probably time to pull out your oscilloscope and make sure the board is generating signals. If so, check the speed/baud/parity. I see that the source runs at 115200bps; maybe try 57600 instead, if your hardware supports it.
An alternative
To use eMPL-client.py with pyserial 3.3, in eMPL-client.py, look for the lines:
if __name__ == "__main__":
if len(sys.argv) == 2:
comport = int(sys.argv[1]) - 1 #### This is the line that triggers the issue
else:
print "usage: " + sys.argv[0] + " port"
sys.exit(-1)
Change the ####-marked line to
comport = sys.argv[1]
(make sure to keep the indentation the same!)
Then, in cmd.exe, run
python eMPL-client.py COM7
with the string port name, e.g., COM7, instead of a port number, e.g., 7.
so i'm trying to learn both python and BACnet using the BACpypes library, and i'm a little bit stuck right now.
I'm trying to make the "WhoIs-IAm" sample application to do an automatic "IAm" broadcast when launched but regarding my newbie skill i'm having trouble to build it.
So there's the sample.
#!/usr/bin/python
"""
This application presents a 'console' prompt to the user asking for Who-Is and I-Am
commands which create the related APDUs, then lines up the coorresponding I-Am
for incoming traffic and prints out the contents.
"""
import sys
from bacpypes.debugging import bacpypes_debugging, ModuleLogger
from bacpypes.consolelogging import ConfigArgumentParser
from bacpypes.consolecmd import ConsoleCmd
from bacpypes.core import run
from bacpypes.pdu import Address, GlobalBroadcast
from bacpypes.app import LocalDeviceObject, BIPSimpleApplication
from bacpypes.apdu import WhoIsRequest, IAmRequest
from bacpypes.basetypes import ServicesSupported
from bacpypes.errors import DecodingError
# some debugging
_debug = 0
_log = ModuleLogger(globals())
# globals
this_device = None
this_application = None
this_console = None
#
# WhoIsIAmApplication
#
class WhoIsIAmApplication(BIPSimpleApplication):
def __init__(self, *args):
if _debug: WhoIsIAmApplication._debug("__init__ %r", args)
BIPSimpleApplication.__init__(self, *args)
# keep track of requests to line up responses
self._request = None
def request(self, apdu):
if _debug: WhoIsIAmApplication._debug("request %r", apdu)
# save a copy of the request
self._request = apdu
# forward it along
BIPSimpleApplication.request(self, apdu)
def confirmation(self, apdu):
if _debug: WhoIsIAmApplication._debug("confirmation %r", apdu)
# forward it along
BIPSimpleApplication.confirmation(self, apdu)
def indication(self, apdu):
if _debug: WhoIsIAmApplication._debug("indication %r", apdu)
if (isinstance(self._request, WhoIsRequest)) and (isinstance(apdu, IAmRequest)):
device_type, device_instance = apdu.iAmDeviceIdentifier
if device_type != 'device':
raise DecodingError, "invalid object type"
if (self._request.deviceInstanceRangeLowLimit is not None) and \
(device_instance < self._request.deviceInstanceRangeLowLimit):
pass
elif (self._request.deviceInstanceRangeHighLimit is not None) and \
(device_instance > self._request.deviceInstanceRangeHighLimit):
pass
else:
# print out the contents
sys.stdout.write('pduSource = ' + repr(apdu.pduSource) + '\n')
sys.stdout.write('iAmDeviceIdentifier = ' + str(apdu.iAmDeviceIdentifier) + '\n')
sys.stdout.write('maxAPDULengthAccepted = ' + str(apdu.maxAPDULengthAccepted) + '\n')
sys.stdout.write('segmentationSupported = ' + str(apdu.segmentationSupported) + '\n')
sys.stdout.write('vendorID = ' + str(apdu.vendorID) + '\n')
sys.stdout.flush()
# forward it along
BIPSimpleApplication.indication(self, apdu)
bacpypes_debugging(WhoIsIAmApplication)
#
# WhoIsIAmConsoleCmd
#
class WhoIsIAmConsoleCmd(ConsoleCmd):
def do_whois(self, args):
"""whois [ <addr>] [ <lolimit> <hilimit> ]"""
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_whois %r", args)
try:
# build a request
request = WhoIsRequest()
if (len(args) == 1) or (len(args) == 3):
request.pduDestination = Address(args[0])
del args[0]
else:
request.pduDestination = GlobalBroadcast()
if len(args) == 2:
request.deviceInstanceRangeLowLimit = int(args[0])
request.deviceInstanceRangeHighLimit = int(args[1])
if _debug: WhoIsIAmConsoleCmd._debug(" - request: %r", request)
# give it to the application
this_application.request(request)
except Exception, e:
WhoIsIAmConsoleCmd._exception("exception: %r", e)
def do_iam(self, args):
"""iam"""
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_iam %r", args)
try:
# build a request
request = IAmRequest()
request.pduDestination = GlobalBroadcast()
# set the parameters from the device object
request.iAmDeviceIdentifier = this_device.objectIdentifier
request.maxAPDULengthAccepted = this_device.maxApduLengthAccepted
request.segmentationSupported = this_device.segmentationSupported
request.vendorID = this_device.vendorIdentifier
if _debug: WhoIsIAmConsoleCmd._debug(" - request: %r", request)
# give it to the application
this_application.request(request)
except Exception, e:
WhoIsIAmConsoleCmd._exception("exception: %r", e)
def do_rtn(self, args):
"""rtn <addr> <net> ... """
args = args.split()
if _debug: WhoIsIAmConsoleCmd._debug("do_rtn %r", args)
# safe to assume only one adapter
adapter = this_application.nsap.adapters[0]
if _debug: WhoIsIAmConsoleCmd._debug(" - adapter: %r", adapter)
# provide the address and a list of network numbers
router_address = Address(args[0])
network_list = [int(arg) for arg in args[1:]]
# pass along to the service access point
this_application.nsap.add_router_references(adapter, router_address, network_list)
bacpypes_debugging(WhoIsIAmConsoleCmd)
#
# __main__
#
try:
# parse the command line arguments
args = ConfigArgumentParser(description=__doc__).parse_args()
if _debug: _log.debug("initialization")
if _debug: _log.debug(" - args: %r", args)
# make a device object
this_device = LocalDeviceObject(
objectName=args.ini.objectname,
objectIdentifier=int(args.ini.objectidentifier),
maxApduLengthAccepted=int(args.ini.maxapdulengthaccepted),
segmentationSupported=args.ini.segmentationsupported,
vendorIdentifier=int(args.ini.vendoridentifier),
)
# build a bit string that knows about the bit names
pss = ServicesSupported()
pss['whoIs'] = 1
pss['iAm'] = 1
pss['readProperty'] = 1
pss['writeProperty'] = 1
# set the property value to be just the bits
this_device.protocolServicesSupported = pss.value
# make a simple application
this_application = WhoIsIAmApplication(this_device, args.ini.address)
# get the services supported
services_supported = this_application.get_services_supported()
if _debug: _log.debug(" - services_supported: %r", services_supported)
# let the device object know
this_device.protocolServicesSupported = services_supported.value
# make a console
this_console = WhoIsIAmConsoleCmd()
_log.debug("running")
run()
except Exception, e:
_log.exception("an error has occurred: %s", e)
finally:
_log.debug("finally")
I just don't know how to call the do_iam so it starts automatically when the app launches.
Any help ?
Thanks.
After the line this_console = WhoIsIAmConsoleCmd(), can you write this_console.do_iam('')?
I know only the very basics of python. I have this project for my INFORMATION STORAGE AND MANAGEMENT subject. I have to give an explanation the following code.
I searched every command used in this script but could not find most of them. The code can be found here:
import glob
import json
import os
import re
import string
import sys
from oslo.config import cfg
from nova import context
from nova.db.sqlalchemy import api as db_api
from nova.db.sqlalchemy import models
from nova import utils
CONF = cfg.CONF
def usage():
print("""
Usage:
python %s --config-file /etc/nova/nova.conf
Note: This script intends to clean up the iSCSI multipath faulty devices
hosted by VNX Block Storage.""" % sys.argv[0])
class FaultyDevicesCleaner(object):
def __init__(self):
# Get host name of Nova computer node.
self.host_name = self._get_host_name()
def _get_host_name(self):
(out, err) = utils.execute('hostname')
return out
def _get_ncpu_emc_target_info_list(self):
target_info_list = []
# Find the targets used by VM on the compute node
bdms = db_api.model_query(context.get_admin_context(),
models.BlockDeviceMapping,
session = db_api.get_session())
bdms = bdms.filter(models.BlockDeviceMapping.connection_info != None)
bdms = bdms.join(models.BlockDeviceMapping.instance).filter_by(
host=string.strip(self.host_name))
for bdm in bdms:
conn_info = json.loads(bdm.connection_info)
if 'data' in conn_info:
if 'target_iqns' in conn_info['data']:
target_iqns = conn_info['data']['target_iqns']
target_luns = conn_info['data']['target_luns']
elif 'target_iqn' in conn_info['data']:
target_iqns = [conn_info['data']['target_iqn']]
target_luns = [conn_info['data']['target_lun']]
else:
target_iqns = []
target_luns = []
for target_iqn, target_lun in zip(target_iqns, target_luns):
if 'com.emc' in target_iqn:
target_info = {
'target_iqn': target_iqn,
'target_lun': target_lun,
}
target_info_list.append(target_info)
return target_info_list
def _get_ncpu_emc_target_info_set(self):
target_info_set = set()
for target_info in self._get_ncpu_emc_target_info_list():
target_iqn = target_info['target_iqn']
target_lun = target_info['target_lun']
target_info_key = "%s-%s" % (target_iqn.rsplit('.', 1)[0],
target_lun)
# target_iqn=iqn.1992-04.com.emc:cx.fnm00130200235.a7
# target_lun=203
# target_info_key=iqn.1992-04.com.emc:cx.fnm00130200235-203
target_info_set.add(target_info_key)
return target_info_set
def _get_target_info_key(self, path):
temp_tuple = path.split('-lun-', 1)
target_lun = temp_tuple[1]
target_iqn = temp_tuple[0].split('-iscsi-')[1]
target_info_key = "%s-%s" % (target_iqn.rsplit('.', 1)[0], target_lun)
# path=/dev/disk/by-path/ip-192.168.3.52:3260-iscsi-iqn.1992-
# 04.com.emc:cx.fnm00130200235.a7-lun-203
# target_info_key=iqn.1992-04.com.emc:cx.fnm00130200235-203
return target_info_key
def _get_non_ncpu_target_info_map(self):
# Group the paths by target_info_key
ncpu_target_info_set = self._get_ncpu_emc_target_info_set()
device_paths = self._get_emc_device_paths()
target_info_map = {}
for path in device_paths:
target_info_key = self._get_target_info_key(path)
if target_info_key in ncpu_target_info_set:
continue
if target_info_key not in target_info_map:
target_info_map[target_info_key] = []
target_info_map[target_info_key].append(path)
return target_info_map
def _all_related_paths_faulty(self, paths):
for path in paths:
real_path = os.path.realpath(path)
out, err = self._run_multipath(['-ll', real_path],
run_as_root=True,
check_exit_code=False)
if 'active ready' in out:
# At least one path is still working
return False
return True
def _delete_all_related_paths(self, paths):
for path in paths:
real_path = os.path.realpath(path)
device_name = os.path.basename(real_path)
device_delete = '/sys/block/%s/device/delete' % device_name
if os.path.exists(device_delete):
# Copy '1' from stdin to the device delete control file
utils.execute('cp', '/dev/stdin', device_delete,
process_input='1', run_as_root=True)
else:
print "Unable to delete %s" % real_path
def _cleanup_faulty_paths(self):
non_ncpu_target_info_map = self._get_non_ncpu_target_info_map()
for paths in non_ncpu_target_info_map.itervalues():
if self._all_related_paths_faulty(paths):
self._delete_all_related_paths(paths)
def _cleanup_faulty_dm_devices(self):
out_ll, err_ll = self._run_multipath(['-ll'],
run_as_root=True,
check_exit_code=False)
# Pattern to split the dm device contents as follows
# Each section starts with a WWN and ends with a line with
# " `-" as the prefix
#
# 3600601601bd032007c097518e96ae411 dm-2 ,
# size=1.0G features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
# `-+- policy='round-robin 0' prio=0 status=active
# `- #:#:#:# - #:# active faulty running
# 36006016020d03200bb93e048f733e411 dm-0 DGC,VRAID
# size=1.0G features='1 queue_if_no_path' hwhandler='1 alua' wp=rw
# |-+- policy='round-robin 0' prio=130 status=active
# | |- 3:0:0:2 sdd 8:48 active ready running
# | `- 5:0:0:2 sdj 8:144 active ready running
# `-+- policy='round-robin 0' prio=10 status=enabled
# |- 4:0:0:2 sdg 8:96 active ready running
# `- 6:0:0:2 sdm 8:192 active ready running
dm_pat = r'([0-9a-fA-F]{30,})[^\n]+,[^\n]*\n[^,]* `-[^\n]*'
dm_m = re.compile(dm_pat)
path_pat = r'- \d+:\d+:\d+:\d+ '
path_m = re.compile(path_pat)
for m in dm_m.finditer(out_ll):
if not path_m.search(m.group(0)):
# Only #:#:#:# remain in the output, all the paths of the dm
# device should have been deleted. No need to keep the device
out_f, err_f = self._run_multipath(['-f', m.group(1)],
run_as_root=True,
check_exit_code=False)
def cleanup(self):
self._cleanup_faulty_paths()
# Make sure the following configuration is in /etc/multipath.conf
# Otherwise, there may be "map in use" failure when deleting
# dm device
#
# defaults {
# flush_on_last_del yes
# }
#
self._cleanup_faulty_dm_devices()
def _get_emc_device_paths(self):
# Find all the EMC iSCSI devices under /dev/disk/by-path
# except LUNZ and partition reference
pattern = '/dev/disk/by-path/ip-*-iscsi-iqn*com.emc*-lun-*'
device_paths = [path for path in glob.glob(pattern)
if ('lun-0' not in path and '-part' not in path)]
return device_paths
def _run_multipath(self, multipath_command, **kwargs):
check_exit_code = kwargs.pop('check_exit_code', 0)
(out, err) = utils.execute('multipath',
*multipath_command,
run_as_root=True,
check_exit_code=check_exit_code)
print ("multipath %(command)s: stdout=%(out)s stderr=%(err)s"
% {'command': multipath_command, 'out': out, 'err': err})
return out, err
if __name__ == "__main__":
if len(sys.argv) != 3 or sys.argv[1] != '--config-file':
usage()
exit(1)
out, err = utils.execute('which', 'multipath', check_exit_code=False)
if 'multipath' not in out:
print('Info: Multipath tools not installed. No cleanup need be done.')
exit(0)
multipath_flush_on_last_del = False
multipath_conf_path = "/etc/multipath.conf"
if os.path.exists(multipath_conf_path):
flush_on_last_del_yes = re.compile(r'\s*flush_on_last_del.*yes')
for line in open(multipath_conf_path, "r"):
if flush_on_last_del_yes.match(line):
multipath_flush_on_last_del = True
break
if not multipath_flush_on_last_del:
print("Warning: 'flush_on_last_del yes' is not seen in"
" /etc/multipath.conf."
" 'map in use' failure may show up during cleanup.")
CONF(sys.argv[1:])
# connect_volume and disconnect_volume in nova/virt/libvirt/volume.py
# need be adjusted to take the same 'external=True' lock for
# synchronization
#utils.synchronized('connect_volume', external=True)
def do_cleanup():
cleaner = FaultyDevicesCleaner()
cleaner.cleanup()
do_cleanup()
https://wiki.python.org/moin/BeginnersGuide/Programmers
http://www.astro.ufl.edu/~warner/prog/python.html
looks like this python version 3 so. go for the tutorials of version three.
try downloading any IDE. eric5 is good by the way.
try executing this file once.
learn indentations
and dynamic variable declaration
do not jump into the ocean first try swimming pool : )
Also Try to learn method declaration.
Python is a bit different than java.
I will give you a hint looks like system call are also made to execute os commands so try looking at subprocess and how its output is directed to an output stream and error stream.
I'm experimenting on converting a makefile from another buildsystem to waf.
I'm trying to direct waf to the directory containing the necessary dlls.
However, when running waf configure:
Checking for library libiconv2 : not found
It can't find the required library.
Directory stucture:
project/
| build/
| inc/
| | XGetopt.h
| | common.h
| | define.h
| | libpst.h
| | libstrfunc.h
| | lzfu.h
| | msg.h
| | timeconv.h
| | vbuf.h
| libs/
| | libiconv2.dll
| | regex2.dll
| src/
| | XGetopt.c
| | debug.c
| | dumpblocks.c
| | getidblock.c
| | libpst.c
| | libstrfunc.c
| | lspst.c
| | lzfu.c
| | readpst.c
| | timeconv.c
| | vbuf.c
| | deltasearch.cpp
| | msg.cpp
| | nick2ldif.cpp
| | pst2dii.cpp
| | pst2ldif.cpp
| | wscript_build
| waf-1.7.10
| wscript
top-level wscript:
#! /usr/bin/env python
VERSION = "0.1"
APPNAME = "readpst"
top = "." # The topmost directory of the waf project
out = "build/temp" # The build directory of the waf project
import os
from waflib import Build
from waflib import ConfigSet
from waflib import Logs
# Variant memory variables
var_path = out + "/variant.txt" # The variant memory file path
default_variant = "debug" # The default if no variant is stored
stored_variant = ""
def options(opt):
'''
A script hook function that defines addtional switch options for the build.
'''
opt.load("compiler_cxx")
def configure(cfg):
'''
A script hook function that configures the build environment.
'''
cfg.load("compiler_cxx")
cfg.find_program("strip")
cfg.env.PREFIX = "."
cfg.env.DEFINES = ["WAF=1"]
cfg.env.FEATURES = [] # Additional features
cfg.env.LIBPATH = [os.path.join(os.getcwd(), "libs")]
print cfg.env.LIBPATH
cfg.define("VERSION", VERSION)
base_env = cfg.env
# Compiler checks
cfg.check_large_file(mandatory = False)
cfg.check_inline()
# Check for the existance and function of specific headers
cfg.check(header_name = "stdint.h")
cfg.check(header_name = "stdio.h")
cfg.check(compiler="cxx", uselib_store="LIBICONV2", mandatory=True, lib="libiconv2")
# Define the debug build environment
cfg.setenv("debug", env = base_env.derive())
cfg.env.CFLAGS = ["-g"]
cfg.define("DEBUG", 1)
cfg.write_config_header("/debug/inc/config.h")
# Define the release build environment
cfg.setenv("release", env = base_env.derive())
cfg.env.CFLAGS = ["-O2"]
cfg.env.FEATURES = ["strip"]
cfg.define("RELEASE", 1)
cfg.write_config_header("/release/inc/config.h")
def pre(ctx):
'''
A callback for before build task start.
'''
print "Starting %sbuild" % (("%s " % ctx.variant) if(ctx.variant) else "")
if ctx.cmd == "install":
print "Installing"
def post(ctx):
'''
A callback for after build task finish.
'''
global var_path
print "Finished %sbuild" % (("%s " % ctx.variant) if(ctx.variant) else "")
env = ConfigSet.ConfigSet()
env.stored_variant = ctx.variant
env.store(var_path)
def build(bld):
'''
A script hook function that specifies the build behaviour.
'''
bld.add_pre_fun(pre)
bld.add_post_fun(post)
bld.recurse\
(
[
"src"
]
)
if bld.cmd != "clean":
bld.logger = Logs.make_logger("test.log", "build") # just to get a clean output
def dist(ctx):
'''
A script hook function that specifies the packaging behaviour.
'''
ctx.base_name = "_".join([APPNAME, VERSION])
ctx.algo = "zip"
file_ex_patterns = \
[
out + "/**",
"**/.waf-1*",
"**/*~",
"**/*.pyc",
"**/*.swp",
"**/.lock-w*"
]
file_in_patterns = \
[
"**/wscript*",
"**/*.h",
"**/*.c",
"**/*.cpp",
"**/*.txt",
]
ctx.files = ctx.path.ant_glob(incl = file_in_patterns, excl = file_ex_patterns)
def set_variant():
'''
A function that facilitates dynamic changing of the Context classes variant member.
It retrieves the stored variant, if existant, otherwise the default.
'''
global default_variant
global stored_variant
global var_path
env = ConfigSet.ConfigSet()
try:
env.load(var_path)
except:
stored_variant = default_variant
else:
if(env.stored_variant):
stored_variant = env.stored_variant
print "Resuming %s variant" % stored_variant
else:
stored_variant = default_variant
def get_variant():
'''
A function that facilitates dynamic changing of the Context classes variant member.
It sets the variant, if undefined, and returns.
'''
global stored_variant
if(not stored_variant):
set_variant()
return stored_variant
class release(Build.BuildContext):
'''
A class that provides the release build.
'''
cmd = "release"
variant = "release"
class debug(Build.BuildContext):
'''
A class that provides the debug build.
'''
cmd = "debug"
variant = "debug"
class default_build(Build.BuildContext):
'''
A class that provides the default variant build.
This is set to debug.
'''
variant = "debug"
class default_clean(Build.CleanContext):
'''
A class that provides the stored variant build clean.
'''
#property
def variant(self):
return get_variant()
class default_install(Build.InstallContext):
'''
A class that provides the stored variant build install.
'''
#property
def variant(self):
return get_variant()
class default_uninstall(Build.UninstallContext):
'''
A class that provides the stored variant build uninstall.
'''
#property
def variant(self):
return get_variant()
# Addtional features
from waflib import Task, TaskGen
class strip(Task.Task):
run_str = "${STRIP} ${SRC}"
color = "BLUE"
#TaskGen.feature("strip")
#TaskGen.after("apply_link")
def add_strip_task(self):
try:
link_task = self.link_task
except:
return
tsk = self.create_task("strip", self.link_task.outputs[0])
You just lack the use variable setup, but this has to be fixed in your child-wscripts, i.e.
bld.program (...,
libpath = ['/usr/lib', 'subpath'], #this has to be relative to the wscript it appears in! (or the root wscript, can not recall)
...,
use = ['iconv2', 'regex2'] )
See section 9.1.2 of the waf book
Alternatively: (and probably the cleaner version)
cfg.check_cc(lib='iconv2', uselib_store="LIBICONV2", mandatory=True)
and then use uselib with
bld.program (...,
libpath = ['/usr/lib', 'subpath'], #this has to be relative to the wscript it appears in! (or the root wscript, can not recall)
...,
uselib = ['LIBICONV2', ...] )
After some consideration, I realised I required further information. The default error information provided by waf seems to be about waf itself, rather than the wscripts or the project.
To rectify this, loggers need to be added. I added loggers to the configure and build functions.
configure logger:
cfg.logger = Logs.make_logger("configure_%s.log" % datetime.date.today().strftime("%Y_%m_%d"), "configure")
build logger:
bld.logger = Logs.make_logger("build_%s.log" % datetime.date.today().strftime("%Y_%m_%d"), "build")
Doing this lead me to that nature of the problems:
['C:\\MinGW64\\bin\\g++.exe', '-Wl,--enable-auto-import', '-Wl,--enable-auto-import', 'test.cpp.1.o', '-o', 'C:\\Users\\Administrator\\Downloads\\libpst-0.6.60\\clean\\build\\temp\\conf_check_5fe204eaa3b3bcb7a9f85e15cebb726e\\testbuild\\testprog.exe', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-LC:\\Users\\Administrator\\Downloads\\libpst-0.6.60\\clean\\libs', '-llibiconv2']
err: c:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.2/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Users\Administrator\Downloads\libpst-0.6.60\clean\libs/libiconv2.dll when searching for -llibiconv2
The library path has been passed correctly to gcc, but the dll is 32bit whereas the gcc installation is 64bit and so it is incompatible.
top-level wscript:
#! /usr/bin/env python
VERSION = "0.1"
APPNAME = "readpst"
top = "." # The topmost directory of the waf project
out = "build/temp" # The build directory of the waf project
import os
import datetime
from waflib import Build
from waflib import ConfigSet
from waflib import Logs
# Variant memory variables
var_path = out + "/variant.txt" # The variant memory file path
default_variant = "debug" # The default if no variant is stored
stored_variant = ""
def options(opt):
'''
A script hook function that defines addtional switch options for the build.
'''
opt.load("compiler_c compiler_cxx")
def configure(cfg):
'''
A script hook function that configures the build environment.
'''
cfg.logger = Logs.make_logger("configure_%s.log" % datetime.date.today().strftime("%Y_%m_%d"), "configure")
cfg.load("compiler_c compiler_cxx")
cfg.find_program("strip")
cfg.env.DEFINES = \
[
"WAF=1",
"HAVE_CONFIG_H=1"
]
cfg.env.FEATURES = [] # Additional features
cfg.env.append_value("LIBPATH", os.path.join(os.getcwd(), "libs"))
cfg.env.append_value("INCLUDES", os.path.join(os.getcwd(), "inc"))
cfg.env.append_value("INCLUDES", os.path.join(os.getcwd(), "inc", "glib-2.0"))
cfg.env.append_value("INCLUDES", os.path.join(os.getcwd(), "inc", "glib-2.0", "glib"))
cfg.env.append_value("INCLUDES", os.path.join(os.getcwd(), "libs", "regex", "2.7", "regex-2.7-src", "src"))
cfg.env.append_value("INCLUDES", os.path.join(os.getcwd(), "libs", "libiconv", "1.9.2", "libiconv-1.9.2-src", "include"))
cfg.define("VERSION", VERSION)
base_env = cfg.env
# Compiler checks
cfg.check_large_file(mandatory = False)
cfg.check_inline()
cfg.multicheck\
(
{"header_name" : "fcntl.h"},
{"header_name" : "iostream"},
{"header_name" : "list"},
{"header_name" : "set"},
{"header_name" : "string"},
{"header_name" : "vector"},
msg = "Checking for standard headers",
mandatory = True
)
cfg.check(header_name = "glib.h", mandatory = False)
cfg.multicheck\
(
{"header_name" : "gsf\\gsf-infile-stdio.h"},
{"header_name" : "gsf\\gsf-infile.h"},
{"header_name" : "gsf\\gsf-input-stdio.h"},
{"header_name" : "gsf\\gsf-outfile-msole.h"},
{"header_name" : "gsf\\gsf-outfile.h"},
{"header_name" : "gsf\\gsf-output-stdio.h"},
{"header_name" : "gsf\\gsf-utils.h"},
msg = "Checking for gsf headers",
mandatory = False
)
# Checking for headers expected in config.h
cfg.check(header_name = "ctype.h", define_name = "HAVE_CTYPE_H" , mandatory = False)
cfg.check(header_name = "dirent.h", define_name = "HAVE_DIRENT_H" , mandatory = False)
cfg.check(header_name = "errno.h", define_name = "HAVE_ERRNO_H" , mandatory = False)
cfg.check(header_name = "gd.h", define_name = "HAVE_GD_H" , mandatory = False)
cfg.check(header_name = "iconv.h", define_name = "HAVE_ICON" , mandatory = False)
cfg.check(header_name = "limits.h", define_name = "HAVE_LIMITS_H" , mandatory = False)
cfg.check(header_name = "regex.h", define_name = "HAVE_REGEX_H" , mandatory = False)
#cfg.check(header_name = "semaphore.h", define_name = "HAVE_SEMAPHORE_H", mandatory = False)
cfg.check(header_name = "signal.h", define_name = "HAVE_SIGNAL_H" , mandatory = False)
cfg.check(header_name = "string.h", define_name = "HAVE_STRING_H" , mandatory = False)
cfg.check(header_name = "sys/shm.h", define_name = "HAVE_SYS_SHM_H" , mandatory = False)
cfg.check(header_name = "sys/stat.h", define_name = "HAVE_SYS_STAT_H" , mandatory = False)
cfg.check(header_name = "sys/types.h", define_name = "HAVE_SYS_TYPES_H", mandatory = False)
cfg.check(header_name = "sys/wait.h", define_name = "HAVE_SYS_WAIT_H" , mandatory = False)
cfg.check(header_name = "wchar.h", define_name = "HAVE_WCHAR_H" , mandatory = False)
cfg.check(header_name = "define.h", mandatory = False)
cfg.check(header_name = "lzfu.h", mandatory = False)
cfg.check(header_name = "msg.h", mandatory = False)
# Check for the existance and function of specific headers
cfg.check_cxx(lib = "libiconv2", uselib_store = "LIBICONV2", mandatory = False)
# Define the debug build environment
cfg.setenv("debug", env = base_env.derive())
cfg.env.append_value("CFLAGS", "-g")
cfg.define("DEBUG", 1)
cfg.write_config_header("/debug/inc/config.h")
# Define the release build environment
cfg.setenv("release", env = base_env.derive())
cfg.env.append_value("CFLAGS", "-02")
cfg.env.FEATURES = ["strip"]
cfg.define("RELEASE", 1)
cfg.write_config_header("/release/inc/config.h")
def pre(ctx):
'''
A callback for before build task start.
'''
print "Starting %sbuild" % (("%s " % ctx.variant) if(ctx.variant) else "")
if ctx.cmd == "install":
print "Installing"
def post(ctx):
'''
A callback for after build task finish.
'''
global var_path
print "Finished %sbuild" % (("%s " % ctx.variant) if(ctx.variant) else "")
env = ConfigSet.ConfigSet()
env.stored_variant = ctx.variant
env.store(var_path)
def build(bld):
'''
A script hook function that specifies the build behaviour.
'''
if bld.cmd != "clean":
bld.logger = Logs.make_logger("build_%s.log" % datetime.date.today().strftime("%Y_%m_%d"), "build")
bld.add_pre_fun(pre)
bld.add_post_fun(post)
bld.recurse\
(
[
"src"
]
)
def dist(ctx):
'''
A script hook function that specifies the packaging behaviour.
'''
ctx.base_name = "_".join([APPNAME, VERSION])
ctx.algo = "zip"
file_ex_patterns = \
[
out + "/**",
"**/.waf-1*",
"**/*~",
"**/*.pyc",
"**/*.swp",
"**/.lock-w*"
]
file_in_patterns = \
[
"**/wscript*",
"**/*.h",
"**/*.c",
"**/*.cpp",
"**/*.txt",
]
ctx.files = ctx.path.ant_glob(incl = file_in_patterns, excl = file_ex_patterns)
def set_variant():
'''
A function that facilitates dynamic changing of the Context classes variant member.
It retrieves the stored variant, if existant, otherwise the default.
'''
global default_variant
global stored_variant
global var_path
env = ConfigSet.ConfigSet()
try:
env.load(var_path)
except:
stored_variant = default_variant
else:
if(env.stored_variant):
stored_variant = env.stored_variant
print "Resuming %s variant" % stored_variant
else:
stored_variant = default_variant
def get_variant():
'''
A function that facilitates dynamic changing of the Context classes variant member.
It sets the variant, if undefined, and returns.
'''
global stored_variant
if(not stored_variant):
set_variant()
return stored_variant
class release(Build.BuildContext):
'''
A class that provides the release build.
'''
cmd = "release"
variant = "release"
class debug(Build.BuildContext):
'''
A class that provides the debug build.
'''
cmd = "debug"
variant = "debug"
class default_build(Build.BuildContext):
'''
A class that provides the default variant build.
This is set to debug.
'''
variant = "debug"
class default_clean(Build.CleanContext):
'''
A class that provides the stored variant build clean.
'''
#property
def variant(self):
return get_variant()
class default_install(Build.InstallContext):
'''
A class that provides the stored variant build install.
'''
#property
def variant(self):
return get_variant()
class default_uninstall(Build.UninstallContext):
'''
A class that provides the stored variant build uninstall.
'''
#property
def variant(self):
return get_variant()
# Addtional features
from waflib import Task, TaskGen
class strip(Task.Task):
run_str = "${STRIP} ${SRC}"
color = "BLUE"
#TaskGen.feature("strip")
#TaskGen.after("apply_link")
def add_strip_task(self):
try:
link_task = self.link_task
except:
return
tsk = self.create_task("strip", self.link_task.outputs[0])