Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
slapos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Aurel
slapos
Commits
f9323891
Commit
f9323891
authored
Jan 25, 2013
by
Jondy Zhao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add inotifyw.c which can work in the windows
parent
6a3e5dc5
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1079 additions
and
3 deletions
+1079
-3
slapos/recipe/inotifyx/inotifyw.c
slapos/recipe/inotifyx/inotifyw.c
+875
-0
slapos/recipe/inotifyx/testInotifyw.py
slapos/recipe/inotifyx/testInotifyw.py
+197
-0
slapos/recipe/librecipe/execute.py
slapos/recipe/librecipe/execute.py
+2
-1
slapos/recipe/librecipe/inotify.py
slapos/recipe/librecipe/inotify.py
+2
-1
slapos/recipe/lockfile.py
slapos/recipe/lockfile.py
+3
-1
No files found.
slapos/recipe/inotifyx/inotifyw.c
0 → 100644
View file @
f9323891
/*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cygwin.h>
/* Avoid select function conflict in the winsock2.h */
#define __INSIDE_CYGWIN__
#include <windows.h>
/* the following are legal, implemented events that user-space can watch for */
#define IN_ACCESS 0x00000001
/* File was accessed */
#define IN_MODIFY 0x00000002
/* File was modified */
#define IN_ATTRIB 0x00000004
/* Metadata changed */
#define IN_CLOSE_WRITE 0x00000008
/* Writtable file was closed */
#define IN_CLOSE_NOWRITE 0x00000010
/* Unwrittable file closed */
#define IN_OPEN 0x00000020
/* File was opened */
#define IN_MOVED_FROM 0x00000040
/* File was moved from X */
#define IN_MOVED_TO 0x00000080
/* File was moved to Y */
#define IN_CREATE 0x00000100
/* Subfile was created */
#define IN_DELETE 0x00000200
/* Subfile was deleted */
#define IN_DELETE_SELF 0x00000400
/* Self was deleted */
#define IN_MOVE_SELF 0x00000800
/* the following are legal events. they are sent as needed to any watch */
#define IN_UNMOUNT 0x00002000
/* Backing fs was unmounted */
#define IN_Q_OVERFLOW 0x00004000
/* Event queued overflowed */
#define IN_IGNORED 0x00008000
/* File was ignored */
/* helper events */
#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
/* close */
#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
/* moves */
/* special flags */
#define IN_ONLYDIR 0x01000000
#define IN_DONT_FOLLOW 0x02000000
#define IN_EXCL_UNLINK 0x04000000
#define IN_ISDIR 0x40000000
/* event occurred against dir */
#define IN_MASK_ADD 0x20000000
#define IN_ONESHOT 0x80000000
/* only send event once */
/*
* All of the events - we build the list by hand so that we can add flags in
* the future and not break backward compatibility. Apps will get only the
* events that they originally wanted. Be sure to add new events here!
*/
#define IN_ALL_EVENTS (IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_CREATE \
| IN_ACCESS | IN_MODIFY)
#define ALIGN_CLUSPROP( count ) (((count) + 3) & ~3)
#define WATCH_BUFFER_SIZE 0x4000
/* 16K */
typedef
struct
_WATCH_DESCRIPTOR
{
int
watch_id
;
/* Internal ID */
uint32_t
mask
;
size_t
len
;
/* Length of path in bytes */
wchar_t
path
[
2
];
/* Variable length */
}
WATCH_DESCRIPTOR
,
*
PWATCH_DESCRIPTOR
;
typedef
struct
_INOTIFY_OBJECT
{
size_t
size
;
/* Total size of object */
int
last_id
;
/* Last watch id */
int
count
;
/* Watch counter */
}
INOTIFY_OBJECT
,
*
PINOTIFY_OBJECT
;
const
char
*
WAIT_EVENT_NAMES
[
2
]
=
{
"monitor_thread_event_notify_path_changed_or_thread_started"
,
"main_thread_event_notify_monitor_thread_quit"
};
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
static
void
print_windows_error
()
{
// Retrieve the system error message for the last-error code
LPVOID
lpMsgBuf
;
DWORD
dw
=
GetLastError
();
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS
,
NULL
,
dw
,
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
(
LPTSTR
)
&
lpMsgBuf
,
0
,
NULL
);
printf
(
"Windows Error:%s
\n
"
,
(
const
char
*
)
lpMsgBuf
);
LocalFree
(
lpMsgBuf
);
}
static
PyObject
*
set_windows_exception
()
{
// Retrieve the system error message for the last-error code
LPVOID
lpMsgBuf
;
DWORD
dw
=
GetLastError
();
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS
,
NULL
,
dw
,
MAKELANGID
(
LANG_NEUTRAL
,
SUBLANG_DEFAULT
),
(
LPTSTR
)
&
lpMsgBuf
,
0
,
NULL
);
PyErr_SetString
(
PyExc_RuntimeError
,
(
const
char
*
)
lpMsgBuf
);
LocalFree
(
lpMsgBuf
);
return
NULL
;
}
static
DWORD
map_mask_to_filter
(
uint32_t
mask
)
{
if
(
mask
&
(
IN_OPEN
|
IN_CLOSE_WRITE
|
IN_CLOSE_NOWRITE
|
IN_DELETE_SELF
))
return
-
1
;
DWORD
filter
=
0
;
if
(
mask
&
IN_ACCESS
)
filter
|=
FILE_NOTIFY_CHANGE_LAST_ACCESS
;
if
(
mask
&
IN_MODIFY
)
filter
|=
FILE_NOTIFY_CHANGE_LAST_WRITE
;
if
(
mask
&
IN_ATTRIB
)
filter
|=
FILE_NOTIFY_CHANGE_ATTRIBUTES
;
if
(
mask
&
(
IN_MOVED_FROM
|
IN_MOVED_TO
|
IN_DELETE
|
IN_CREATE
))
filter
|=
FILE_NOTIFY_CHANGE_FILE_NAME
|
FILE_NOTIFY_CHANGE_DIR_NAME
;
return
filter
;
}
static
uint32_t
map_action_to_mask
(
uint32_t
mask
,
DWORD
action
)
{
uint32_t
result
=
0
;
switch
(
action
){
case
FILE_ACTION_ADDED
:
return
IN_CREATE
;
case
FILE_ACTION_REMOVED
:
return
IN_DELETE
;
case
FILE_ACTION_MODIFIED
:
if
(
mask
&
IN_ATTRIB
)
result
|=
IN_ATTRIB
;
if
(
mask
&
IN_ACCESS
)
result
|=
IN_ACCESS
;
if
(
mask
&
IN_MODIFY
)
result
|=
IN_MODIFY
;
return
result
;
case
FILE_ACTION_RENAMED_OLD_NAME
:
return
IN_MOVED_FROM
;
case
FILE_ACTION_RENAMED_NEW_NAME
:
return
IN_MOVED_TO
;
default:
return
0
;
}
}
static
void
free_cobject
(
void
*
p
)
{
assert
(
p
);
FREE
(
p
);
}
static
PyObject
*
inotifyw_init
(
PyObject
*
self
,
PyObject
*
args
)
{
PINOTIFY_OBJECT
pnotify
=
(
PINOTIFY_OBJECT
)
MALLOC
(
WATCH_BUFFER_SIZE
);
if
(
pnotify
==
NULL
)
return
PyErr_NoMemory
();
ZeroMemory
((
void
*
)
pnotify
,
WATCH_BUFFER_SIZE
);
pnotify
->
size
=
sizeof
(
INOTIFY_OBJECT
);
return
PyCObject_FromVoidPtr
(
pnotify
,
free_cobject
);
}
static
PyObject
*
inotifyw_add_watch
(
PyObject
*
self
,
PyObject
*
args
)
{
PyObject
*
pobj
=
NULL
;
char
*
path
;
ssize_t
size
;
uint32_t
mask
;
PINOTIFY_OBJECT
pnotify
;
PWATCH_DESCRIPTOR
pwatch
;
mask
=
IN_ALL_EVENTS
;
if
(
!
PyArg_ParseTuple
(
args
,
"Os|i"
,
&
pobj
,
&
path
,
&
mask
))
return
NULL
;
if
(
!
PyCObject_Check
(
pobj
))
return
NULL
;
/* Check mask by mapping mask to NotifyFilter */
DWORD
filter
=
map_mask_to_filter
(
mask
);
if
((
filter
==
-
1
)
||
(
filter
==
0
))
{
PyErr_SetString
(
PyExc_RuntimeError
,
"unsupport flags"
);
return
NULL
;
}
/* Map posix path to win path.
In the Windows API, the maximum length for a path is MAX_PATH,
which is defined as 260 characters.
The Windows API has many functions that also have Unicode
versions to permit an extended-length path for a maximum total
path length of 32,767 characters. This type of path is composed
of components separated by backslashes, each up to the value
returned in the lpMaximumComponentLength parameter of the
GetVolumeInformation function (this value is commonly 255
characters). To specify an extended-length path, use the "\\?\"
prefix. For example, "\\?\D:\very long path".
http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx
*/
size
=
cygwin_conv_path
(
CCP_POSIX_TO_WIN_W
,
path
,
NULL
,
0
);
if
(
size
<
0
){
PyErr_SetString
(
PyExc_RuntimeError
,
"cygwin_conv_path failed"
);
return
NULL
;
}
/* Let size to be DWORD-Aligned */
size
=
ALIGN_CLUSPROP
(
size
);
pnotify
=
(
PINOTIFY_OBJECT
)
PyCObject_AsVoidPtr
(
pobj
);
if
(
pnotify
->
size
+
sizeof
(
WATCH_DESCRIPTOR
)
+
size
>
WATCH_BUFFER_SIZE
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"no sapce to add watch"
);
return
NULL
;
}
pwatch
=
(
PWATCH_DESCRIPTOR
)((
void
*
)
pnotify
+
pnotify
->
size
);
if
(
cygwin_conv_path
(
CCP_POSIX_TO_WIN_W
,
path
,
pwatch
->
path
,
size
)){
PyErr_SetString
(
PyExc_RuntimeError
,
"cygwin_conv_path"
);
return
NULL
;
}
pwatch
->
watch_id
=
pnotify
->
last_id
;
pwatch
->
mask
=
mask
;
pwatch
->
len
=
size
;
pnotify
->
size
+=
sizeof
(
WATCH_DESCRIPTOR
)
+
size
;
pnotify
->
last_id
++
;
pnotify
->
count
++
;
return
Py_BuildValue
(
"i"
,
pwatch
->
watch_id
);
}
static
PyObject
*
inotifyw_rm_watch
(
PyObject
*
self
,
PyObject
*
args
)
{
PyObject
*
pobj
=
NULL
;
uint32_t
watch_id
;
int
index
;
size_t
size
;
size_t
offset
;
PWATCH_DESCRIPTOR
pwatch
;
PINOTIFY_OBJECT
pnotify
;
if
(
!
PyArg_ParseTuple
(
args
,
"Oi"
,
&
pobj
,
&
watch_id
))
return
NULL
;
if
(
!
PyCObject_Check
(
pobj
))
return
NULL
;
pnotify
=
(
PINOTIFY_OBJECT
)
PyCObject_AsVoidPtr
(
pobj
);
pwatch
=
(
PWATCH_DESCRIPTOR
)((
void
*
)
pnotify
+
sizeof
(
INOTIFY_OBJECT
));
index
=
pnotify
->
count
;
while
(
index
)
{
size
=
+
sizeof
(
WATCH_DESCRIPTOR
)
+
pwatch
->
len
;
if
(
pwatch
->
watch_id
==
watch_id
){
pnotify
->
count
--
;
pnotify
->
size
-=
size
;
memcpy
((
void
*
)
pwatch
,
(
void
*
)
pwatch
+
size
,
pnotify
->
size
-
((
void
*
)
pwatch
-
(
void
*
)
pnotify
)
);
return
Py_BuildValue
(
"i"
,
pnotify
->
count
-
index
+
1
);
}
index
--
;
pwatch
=
(
PWATCH_DESCRIPTOR
)((
void
*
)
pwatch
+
size
);
}
PyErr_SetString
(
PyExc_RuntimeError
,
"no such watch descriptor"
);
return
NULL
;
}
static
int
format_inotify_event
(
PyObject
*
pret
,
PWATCH_DESCRIPTOR
pwatch
,
PFILE_NOTIFY_INFORMATION
pinfo
)
{
PyObject
*
value
=
NULL
;
ssize_t
size
;
char
*
target_path
;
wchar_t
*
wfilename
;
uint32_t
mask
;
int
counter
=
0
;
while
(
1
)
{
mask
=
map_action_to_mask
(
pwatch
->
mask
,
pinfo
->
Action
);
if
(
pwatch
->
mask
&
mask
){
counter
++
;
if
(
pinfo
->
FileNameLength
)
{
/* Null-Terminated File Name */
wfilename
=
(
wchar_t
*
)
MALLOC
(
pinfo
->
FileNameLength
+
2
);
if
(
wfilename
==
NULL
)
{
PyErr_NoMemory
();
return
-
1
;
}
memcpy
(
wfilename
,
pinfo
->
FileName
,
pinfo
->
FileNameLength
);
*
(
wfilename
+
pinfo
->
FileNameLength
/
2
)
=
0
;
/* Conversion from incoming Win32 path given as wchar_t *win32 to
POSIX path. First ask how big the output buffer has to be and
allocate space dynamically. */
size
=
cygwin_conv_path
(
CCP_WIN_W_TO_POSIX
,
wfilename
,
NULL
,
0
);
if
(
size
<
0
)
{
FREE
(
wfilename
);
PyErr_SetString
(
PyExc_RuntimeError
,
"cygwin_conv_path failed"
);
return
-
1
;
}
target_path
=
(
char
*
)
MALLOC
(
size
);
if
(
target_path
==
NULL
)
{
FREE
(
wfilename
);
PyErr_NoMemory
();
return
-
1
;
}
ZeroMemory
((
void
*
)
target_path
,
size
);
if
(
cygwin_conv_path
(
CCP_WIN_W_TO_POSIX
|
CCP_RELATIVE
,
wfilename
,
target_path
,
size
))
{
PyErr_SetString
(
PyExc_RuntimeError
,
"cygwin_conv_path failed"
);
return
-
1
;
}
value
=
Py_BuildValue
(
"iiis"
,
pwatch
->
watch_id
,
mask
,
0
,
target_path
);
FREE
(
target_path
);
}
else
value
=
Py_BuildValue
(
"iiiO"
,
pwatch
->
watch_id
,
mask
,
0
,
Py_None
);
FREE
(
wfilename
);
if
((
value
==
NULL
)
||
(
PyList_Append
(
pret
,
value
)
<
0
))
return
-
1
;
}
if
(
pinfo
->
NextEntryOffset
==
0
)
break
;
pinfo
=
(
PFILE_NOTIFY_INFORMATION
)((
void
*
)
pinfo
+
pinfo
->
NextEntryOffset
);
}
return
counter
;
}
DWORD
WINAPI
ThreadProc
(
LPVOID
lpParam
)
{
DWORD
filter
=
0
;
BOOL
subtree
=
TRUE
;
int
i
;
uint32_t
count
;
void
*
pbuffer
=
NULL
;
size_t
bufsize
;
HANDLE
*
phandles
;
HANDLE
*
events
;
HANDLE
*
pevents
;
PWATCH_DESCRIPTOR
*
pwatches
;
OVERLAPPED
*
poverlappeds
;
void
*
output
;
PFILE_NOTIFY_INFORMATION
pinfo
;
size_t
outbufsize
=
0
;
HRESULT
hr
=
S_OK
;
PINOTIFY_OBJECT
pnotify
=
(
PINOTIFY_OBJECT
)
lpParam
;
DWORD
dwExitCode
=
0
;
/* Allocate space for handles, events and overlappeds */
count
=
pnotify
->
count
;
bufsize
=
(
sizeof
(
OVERLAPPED
)
+
sizeof
(
PWATCH_DESCRIPTOR
)
+
sizeof
(
HANDLE
))
*
count
+
sizeof
(
HANDLE
)
*
2
;
pbuffer
=
MALLOC
(
bufsize
);
if
(
pbuffer
==
NULL
)
return
1
;
ZeroMemory
(
pbuffer
,
bufsize
);
phandles
=
(
HANDLE
*
)
pbuffer
;
events
=
phandles
+
count
;
pevents
=
events
+
2
;
pwatches
=
(
PWATCH_DESCRIPTOR
*
)((
void
*
)
pevents
+
sizeof
(
HANDLE
)
*
count
);
poverlappeds
=
(
OVERLAPPED
*
)((
void
*
)
pwatches
+
sizeof
(
PWATCH_DESCRIPTOR
)
*
count
);
/* Allocate space for output, after detect path changed, the
information will be copied to rest space of pnotify object. */
output
=
(
void
*
)
pnotify
+
ALIGN_CLUSPROP
(
pnotify
->
size
);
outbufsize
=
WATCH_BUFFER_SIZE
-
ALIGN_CLUSPROP
(
pnotify
->
size
);
pinfo
=
(
PFILE_NOTIFY_INFORMATION
)
MALLOC
(
outbufsize
);
if
(
pinfo
==
NULL
)
{
FREE
(
pbuffer
);
return
2
;
}
ZeroMemory
(
pinfo
,
outbufsize
);
events
[
0
]
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
WAIT_EVENT_NAMES
[
0
]);
events
[
1
]
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
WAIT_EVENT_NAMES
[
1
]);
if
((
events
[
0
]
==
NULL
)
||
(
events
[
1
]
==
NULL
))
{
FREE
(
pbuffer
);
FREE
(
pinfo
);
return
3
;
}
/* Notify main thread it's ready */
if
(
!
SetEvent
(
events
[
0
]))
{
CloseHandle
(
events
[
0
]);
CloseHandle
(
events
[
1
]);
FREE
(
pbuffer
);
FREE
(
pinfo
);
return
4
;
}
// Init watch events
PWATCH_DESCRIPTOR
p
;
p
=
(
PWATCH_DESCRIPTOR
)((
void
*
)
pnotify
+
sizeof
(
INOTIFY_OBJECT
));
/* pinfo is used as parameter "lpBuffer" of ReadDirectoryChangesW
lpBuffer [out]
A pointer to the DWORD-aligned formatted buffer in which the read
results are to be returned. The structure of this buffer is
defined by the FILE_NOTIFY_INFORMATION structure.
Read above document carefully, lpBuffer must BE DWORD-aligned. I
have made this mistake, and be curious why ReadDirectoryChangesW
doesn't work. I had spent almost one day to resolve it before I
knew it. You'll not aware of it when using malloc, but allocate
space by yourself, it's coming.
*/
for
(
i
=
0
;
i
<
count
;
i
++
){
// Open directory handle
phandles
[
i
]
=
CreateFileW
(
p
->
path
,
FILE_LIST_DIRECTORY
|
GENERIC_READ
,
FILE_SHARE_READ
|
FILE_SHARE_WRITE
|
FILE_SHARE_DELETE
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_BACKUP_SEMANTICS
|
FILE_FLAG_OVERLAPPED
,
NULL
);
if
(
phandles
[
i
]
==
INVALID_HANDLE_VALUE
){
hr
=
S_FALSE
;
dwExitCode
=
5
;
break
;
}
pevents
[
i
]
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
NULL
);
if
(
pevents
[
i
]
==
NULL
){
hr
=
S_FALSE
;
dwExitCode
=
6
;
break
;
}
pwatches
[
i
]
=
p
;
p
=
(
PWATCH_DESCRIPTOR
)((
void
*
)
p
+
sizeof
(
WATCH_DESCRIPTOR
)
+
p
->
len
);
}
/* Loop for waiting changes of watched path */
DWORD
waitResult
;
DWORD
dwBytesReturned
;
while
(
hr
==
S_OK
)
{
ZeroMemory
((
void
*
)
poverlappeds
,
sizeof
(
OVERLAPPED
)
*
count
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
poverlappeds
[
i
].
hEvent
=
pevents
[
i
];
//
// Get information that describes the most recent file change
// This call will return immediately and will set
// overlapped.hEvent when it has completed
//
if
(
0
==
ReadDirectoryChangesW
(
phandles
[
i
],
pinfo
,
outbufsize
,
subtree
,
map_mask_to_filter
(
pwatches
[
i
]
->
mask
),
&
dwBytesReturned
,
(
LPOVERLAPPED
)(
poverlappeds
+
i
),
NULL
)){
hr
=
S_FALSE
;
dwExitCode
=
7
;
break
;
}
}
if
(
S_OK
==
hr
)
{
waitResult
=
WaitForMultipleObjects
(
count
+
1
,
events
+
1
,
FALSE
,
INFINITE
);
if
(
waitResult
==
WAIT_OBJECT_0
)
break
;
else
if
((
waitResult
>
WAIT_OBJECT_0
)
&&
(
waitResult
<
WAIT_OBJECT_0
+
count
+
1
))
{
// Retrieve result from overlapped structure
DWORD
dwBytesTransferred
=
0
;
uint32_t
index
=
waitResult
-
1
;
if
(
GetOverlappedResult
(
phandles
[
index
],
poverlappeds
+
index
,
&
dwBytesTransferred
,
TRUE
))
{
/* Check whether main thread is idle */
if
(
WaitForSingleObject
(
events
[
0
],
0
)
==
WAIT_TIMEOUT
)
{
((
PWATCH_DESCRIPTOR
*
)
output
)[
0
]
=
pwatches
[
index
];
memcpy
(
output
+
sizeof
(
PWATCH_DESCRIPTOR
),
pinfo
,
dwBytesTransferred
);
if
(
SetEvent
(
events
[
0
]))
{
ResetEvent
(
pevents
[
index
]);
continue
;
}
}
}
}
// case 1: WaitForMultipleObjects failed
// case 2: GetOverlappedResult failed
// case 3: Main thread haven't reset events[0]
// case 4: SetEvent events[0] failed
dwExitCode
=
8
;
hr
=
S_FALSE
;
}
}
// Cleanup
assert
(
pbuffer
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
(
pevents
[
i
])
CloseHandle
(
pevents
[
i
]);
if
(
phandles
[
i
])
CloseHandle
(
phandles
[
i
]);
}
assert
(
pinfo
);
FREE
(
pinfo
);
FREE
(
pbuffer
);
SetEvent
(
events
[
0
]);
CloseHandle
(
events
[
0
]);
CloseHandle
(
events
[
1
]);
return
dwExitCode
;
}
static
PyObject
*
inotifyw_get_events
(
PyObject
*
self
,
PyObject
*
args
)
{
PyObject
*
pobj
;
float
timeouts
=
INFINITE
;
PyObject
*
retvalue
=
NULL
;
int
retcode
;
void
*
pwatch
;
void
*
pinfo
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|f"
,
&
pobj
,
&
timeouts
))
return
NULL
;
if
(
!
PyCObject_Check
(
pobj
))
return
NULL
;
PINOTIFY_OBJECT
pnotify
=
(
PINOTIFY_OBJECT
)
PyCObject_AsVoidPtr
(
pobj
);
if
(
pnotify
->
count
==
0
)
return
PyList_New
(
0
);
HANDLE
hevents
[
2
];
DWORD
dwWaitResult
;
DWORD
dwMilliseconds
;
DWORD
dwThreadID
;
DWORD
dwExitCode
;
HANDLE
hThread
=
NULL
;
HRESULT
hr
=
S_OK
;
Py_BEGIN_ALLOW_THREADS
;
hevents
[
0
]
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
WAIT_EVENT_NAMES
[
0
]);
hevents
[
1
]
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
WAIT_EVENT_NAMES
[
1
]);
Py_END_ALLOW_THREADS
;
if
((
hevents
[
0
]
==
NULL
)
||
(
hevents
[
0
]
==
NULL
)){
set_windows_exception
();
hr
=
S_FALSE
;
}
// Create monitor thread to watch path changes
if
(
hr
==
S_OK
)
{
hThread
=
CreateThread
(
NULL
,
// default security
0
,
// stack size
ThreadProc
,
// name of the thread function
(
LPVOID
)
pnotify
,
0
,
// startup flags
&
dwThreadID
);
if
(
hThread
==
NULL
){
set_windows_exception
();
hr
=
S_FALSE
;
}
}
/* Wait monitor thread to work for 3 seconds */
if
(
hr
==
S_OK
)
{
Py_BEGIN_ALLOW_THREADS
;
dwWaitResult
=
WaitForSingleObject
(
hevents
[
0
],
3000
);
Py_END_ALLOW_THREADS
;
if
(
!
((
dwWaitResult
==
WAIT_OBJECT_0
)
&&
ResetEvent
(
hevents
[
0
])))
{
// case 1: WaitForSingleObject failed
// case 2: monitor thread exception
// case 3: monitor thread hasn't been executed, maybe os is busy
// case 4: ResetEvent failed
PyErr_SetString
(
PyExc_RuntimeError
,
"inotifyw starts exception"
);
hr
=
S_FALSE
;
}
}
if
(
hr
==
S_OK
)
{
dwMilliseconds
=
timeouts
==
INFINITE
?
INFINITE
:
timeouts
*
1000
;
pwatch
=
(
void
*
)
pnotify
+
ALIGN_CLUSPROP
(
pnotify
->
size
);
pinfo
=
pwatch
+
sizeof
(
PWATCH_DESCRIPTOR
);
}
// Loop for change notifications
while
(
hr
==
S_OK
)
{
Py_BEGIN_ALLOW_THREADS
;
dwWaitResult
=
WaitForSingleObject
(
hevents
[
0
],
dwMilliseconds
);
Py_END_ALLOW_THREADS
;
Py_BEGIN_ALLOW_THREADS
;
if
(
!
(
GetExitCodeThread
(
hThread
,
&
dwExitCode
)
\
&&
(
dwExitCode
==
STILL_ACTIVE
)))
hr
=
S_FALSE
;
Py_END_ALLOW_THREADS
;
if
(
S_FALSE
==
hr
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"monitor thread exception"
);
break
;
}
if
(
dwWaitResult
==
WAIT_TIMEOUT
)
{
retvalue
=
PyList_New
(
0
);
break
;
}
else
if
(
dwWaitResult
==
WAIT_OBJECT_0
)
{
if
((
retvalue
=
PyList_New
(
0
))
==
NULL
)
{
hr
=
S_FALSE
;
break
;
}
retcode
=
format_inotify_event
(
retvalue
,
((
PWATCH_DESCRIPTOR
*
)
pwatch
)[
0
],
(
PFILE_NOTIFY_INFORMATION
)
pinfo
);
if
(
retcode
>
0
)
break
;
else
if
(
retcode
==
0
)
{
if
(
dwMilliseconds
==
INFINITE
){
Py_XDECREF
(
retvalue
);
retvalue
=
NULL
;
if
(
ResetEvent
(
hevents
[
0
]))
continue
;
set_windows_exception
();
hr
=
S_FALSE
;
}
break
;
}
else
hr
=
S_FALSE
;
}
else
{
set_windows_exception
();
hr
==
S_FALSE
;
}
}
// Notify monitor thread to quit
if
(
hThread
){
Py_BEGIN_ALLOW_THREADS
;
SetEvent
(
hevents
[
1
]);
CloseHandle
(
hThread
);
Py_END_ALLOW_THREADS
;
}
// Clean up
Py_BEGIN_ALLOW_THREADS
;
CloseHandle
(
hevents
[
0
]);
CloseHandle
(
hevents
[
1
]);
Py_END_ALLOW_THREADS
;
return
retvalue
;
}
static
PyMethodDef
InotifyMethods
[]
=
{
{
"init"
,
inotifyw_init
,
METH_VARARGS
,
(
"init()
\n\n
"
"Initialize an inotify instance and return a PyCObject, When this
\n
"
"PyCObject is reclaimed, GC will free the memory.
\n
"
)
},
{
"add_watch"
,
inotifyw_add_watch
,
METH_VARARGS
,
(
"add_watch(fd, path[, mask])
\n\n
"
"Add a watch for path and return the watch descriptor.
\n
"
"fd should be the object returned by init.
\n
"
"If left unspecified, mask defaults to IN_ALL_EVENTS.
\n
"
"See the inotifyw documentation for details."
)
},
{
"rm_watch"
,
inotifyw_rm_watch
,
METH_VARARGS
,
(
"rm_watch(fd, wd)
\n\n
"
"Remove the watch associated with watch descriptor wd.
\n
"
"fd should be the object returned by init.
\n
"
)
},
{
"get_events"
,
inotifyw_get_events
,
METH_VARARGS
,
"get_events(fd[, timeout])
\n\n
"
"Read events from inotify and return a list of tuples "
"(wd, mask, cookie, name).
\n
"
"The name field is None if no name is associated with the inotify event.
\n
"
"Timeout specifies a timeout in seconds (as an integer or float).
\n
"
"If left unspecified, there is no timeout and get_events will block
\n
"
"indefinitely. If timeout is zero, get_events will not block."
},
{
NULL
,
NULL
,
0
,
NULL
}
};
PyMODINIT_FUNC
initinotifyw
(
void
)
{
PyObject
*
module
;
module
=
Py_InitModule3
(
"inotifyw"
,
InotifyMethods
,
"Low-level interface to inotify in the Windows."
);
if
(
module
==
NULL
)
return
;
PyModule_AddIntConstant
(
module
,
"IN_ACCESS"
,
IN_ACCESS
);
PyModule_AddIntConstant
(
module
,
"IN_MODIFY"
,
IN_MODIFY
);
PyModule_AddIntConstant
(
module
,
"IN_ATTRIB"
,
IN_ATTRIB
);
PyModule_AddIntConstant
(
module
,
"IN_CLOSE_WRITE"
,
IN_CLOSE_WRITE
);
PyModule_AddIntConstant
(
module
,
"IN_CLOSE_NOWRITE"
,
IN_CLOSE_NOWRITE
);
PyModule_AddIntConstant
(
module
,
"IN_CLOSE"
,
IN_CLOSE
);
PyModule_AddIntConstant
(
module
,
"IN_OPEN"
,
IN_OPEN
);
PyModule_AddIntConstant
(
module
,
"IN_MOVED_FROM"
,
IN_MOVED_FROM
);
PyModule_AddIntConstant
(
module
,
"IN_MOVED_TO"
,
IN_MOVED_TO
);
PyModule_AddIntConstant
(
module
,
"IN_MOVE"
,
IN_MOVE
);
PyModule_AddIntConstant
(
module
,
"IN_CREATE"
,
IN_CREATE
);
PyModule_AddIntConstant
(
module
,
"IN_DELETE"
,
IN_DELETE
);
PyModule_AddIntConstant
(
module
,
"IN_DELETE_SELF"
,
IN_DELETE_SELF
);
PyModule_AddIntConstant
(
module
,
"IN_MOVE_SELF"
,
IN_MOVE_SELF
);
PyModule_AddIntConstant
(
module
,
"IN_UNMOUNT"
,
IN_UNMOUNT
);
PyModule_AddIntConstant
(
module
,
"IN_Q_OVERFLOW"
,
IN_Q_OVERFLOW
);
PyModule_AddIntConstant
(
module
,
"IN_IGNORED"
,
IN_IGNORED
);
PyModule_AddIntConstant
(
module
,
"IN_ONLYDIR"
,
IN_ONLYDIR
);
PyModule_AddIntConstant
(
module
,
"IN_DONT_FOLLOW"
,
IN_DONT_FOLLOW
);
PyModule_AddIntConstant
(
module
,
"IN_MASK_ADD"
,
IN_MASK_ADD
);
PyModule_AddIntConstant
(
module
,
"IN_ISDIR"
,
IN_ISDIR
);
PyModule_AddIntConstant
(
module
,
"IN_ONESHOT"
,
IN_ONESHOT
);
PyModule_AddIntConstant
(
module
,
"IN_ALL_EVENTS"
,
IN_ALL_EVENTS
);
}
slapos/recipe/inotifyx/testInotifyw.py
0 → 100755
View file @
f9323891
# -*- coding: utf-8 -*-
import
sys
import
os
import
tempfile
import
shutil
import
test.test_support
inotifyw
=
test
.
test_support
.
import_module
(
'inotifyw'
)
threading
=
test
.
test_support
.
import_module
(
'threading'
)
import
unittest
class
BaseTestCase
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
_threads
=
test
.
test_support
.
threading_setup
()
self
.
work_path
=
tempfile
.
mkdtemp
()
self
.
work_path2
=
tempfile
.
mkdtemp
()
def
tearDown
(
self
):
test
.
test_support
.
threading_cleanup
(
*
self
.
_threads
)
test
.
test_support
.
reap_children
()
shutil
.
rmtree
(
self
.
work_path
)
shutil
.
rmtree
(
self
.
work_path2
)
class
InotifywTests
(
BaseTestCase
):
def
test_add_watch
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
self
.
assertEquals
(
wd
,
0
)
def
test_rm_watch
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
self
.
assertEquals
(
inotifyw
.
rm_watch
(
fd
,
wd
),
0
)
def
test_rm_watch_not_exists
(
self
):
fd
=
inotifyw
.
init
()
self
.
assertRaises
(
RuntimeError
,
inotifyw
.
rm_watch
,
fd
,
0
)
def
test_rm_add_more_watches
(
self
):
fd
=
inotifyw
.
init
()
wd1
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
wd2
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path2
)
self
.
assertEquals
((
wd1
,
wd2
),
(
0
,
1
))
self
.
assertEquals
(
inotifyw
.
rm_watch
(
fd
,
wd2
),
1
)
self
.
assertEquals
(
inotifyw
.
rm_watch
(
fd
,
wd1
),
0
)
def
test_rm_add_more_watches_2
(
self
):
fd
=
inotifyw
.
init
()
wd1
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
wd2
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path2
)
self
.
assertEquals
((
wd1
,
wd2
),
(
0
,
1
))
self
.
assertEquals
(
inotifyw
.
rm_watch
(
fd
,
wd1
),
0
)
self
.
assertEquals
(
inotifyw
.
rm_watch
(
fd
,
wd2
),
0
)
def
test_get_events_nowait
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
self
.
assertEquals
(
inotifyw
.
get_events
(
fd
,
0
),
[])
def
test_get_events_timeout
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
self
.
assertEquals
(
inotifyw
.
get_events
(
fd
,
1.0
),
[])
def
test_notify_any_change
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
name
=
'a'
path
=
os
.
path
.
join
(
self
.
work_path
,
name
)
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
mkdir
(
path
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_CREATE
,
0
,
name
))
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
rmdir
(
path
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_DELETE
,
0
,
name
))
t
=
threading
.
Timer
(
1.0
,
lambda
:
tempfile
.
mkstemp
(
dir
=
self
.
work_path
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
],
inotifyw
.
IN_CREATE
)
filename
=
e
[
0
][
3
]
wd
=
e
[
0
][
0
]
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
remove
(
os
.
path
.
join
(
self
.
work_path
,
filename
)))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
],
(
wd
,
inotifyw
.
IN_DELETE
,
0
,
filename
))
def
test_notify_create_path
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
,
inotifyw
.
IN_CREATE
)
name
=
'a'
path
=
os
.
path
.
join
(
self
.
work_path
,
name
)
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
mkdir
(
path
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_CREATE
,
0
,
name
))
def
test_notify_change_attribute
(
self
):
fd
=
inotifyw
.
init
()
fullpath
=
tempfile
.
mkstemp
(
dir
=
self
.
work_path
)[
1
]
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
,
inotifyw
.
IN_ATTRIB
)
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
chmod
(
fullpath
,
0777
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_ATTRIB
,
0
,
os
.
path
.
split
(
fullpath
)[
1
]))
def
test_notify_in_modify
(
self
):
fd
=
inotifyw
.
init
()
fullpath
=
tempfile
.
mkstemp
(
dir
=
self
.
work_path
)[
1
]
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
,
inotifyw
.
IN_MODIFY
)
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
utime
(
fullpath
,
None
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_MODIFY
,
0
,
os
.
path
.
split
(
fullpath
)[
1
]))
def
test_notify_in_access
(
self
):
fd
=
inotifyw
.
init
()
fullpath
=
tempfile
.
mkstemp
(
dir
=
self
.
work_path
)[
1
]
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
,
inotifyw
.
IN_ACCESS
)
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
utime
(
fullpath
,
None
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_ACCESS
,
0
,
os
.
path
.
split
(
fullpath
)[
1
]))
def
test_notify_delete_file
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
,
inotifyw
.
IN_DELETE
)
name
=
'a'
path
=
os
.
path
.
join
(
self
.
work_path
,
name
)
os
.
mkdir
(
path
)
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
rmdir
(
path
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_DELETE
,
0
,
name
))
fullpath
=
tempfile
.
mkstemp
(
dir
=
self
.
work_path
)[
1
]
t
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
remove
(
fullpath
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
],
(
wd
,
inotifyw
.
IN_DELETE
,
0
,
os
.
path
.
split
(
fullpath
)[
1
]))
def
test_notify_many_changes
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
)
names
=
(
'a'
,
'b'
,
'c'
)
fullnames
=
[
os
.
path
.
join
(
self
.
work_path
,
s
)
for
s
in
names
]
ts
=
[]
for
s
in
fullnames
:
ts
.
append
(
threading
.
Timer
(
1.0
,
lambda
path
=
s
:
os
.
mkdir
(
path
)))
[
t
.
start
()
for
t
in
ts
]
e
=
inotifyw
.
get_events
(
fd
,
5.0
)
self
.
assertEquals
(
len
(
e
),
1
)
def
test_notify_until_delete_file
(
self
):
fd
=
inotifyw
.
init
()
wd
=
inotifyw
.
add_watch
(
fd
,
self
.
work_path
,
inotifyw
.
IN_DELETE
)
name
=
'a'
t1
=
threading
.
Timer
(
1.0
,
lambda
:
os
.
mkdir
(
os
.
path
.
join
(
self
.
work_path
,
name
)))
t2
=
threading
.
Timer
(
3.0
,
lambda
:
os
.
rmdir
(
os
.
path
.
join
(
self
.
work_path
,
name
)))
[
t
.
start
()
for
t
in
(
t1
,
t2
)
]
e
=
inotifyw
.
get_events
(
fd
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_DELETE
,
0
,
name
))
def
test_notify_many_watches
(
self
):
fd
=
inotifyw
.
init
()
patha
=
self
.
work_path2
pathb
=
self
.
work_path
wd1
=
inotifyw
.
add_watch
(
fd
,
patha
,
inotifyw
.
IN_CREATE
)
wd2
=
inotifyw
.
add_watch
(
fd
,
pathb
,
inotifyw
.
IN_CREATE
)
name
=
'test'
fullpath
=
os
.
path
.
join
(
pathb
,
name
)
t
=
threading
.
Timer
(
1.5
,
lambda
:
os
.
mkdir
(
fullpath
))
t
.
start
()
e
=
inotifyw
.
get_events
(
fd
,
3.0
)
self
.
assertEquals
(
e
[
0
][
1
:],
(
inotifyw
.
IN_CREATE
,
0
,
name
))
if
__name__
==
"__main__"
:
# unittest.main()
loader
=
unittest
.
TestLoader
()
# loader.testMethodPrefix = 'test_notify_many_watches'
suite
=
loader
.
loadTestsFromTestCase
(
InotifywTests
)
unittest
.
TextTestRunner
(
verbosity
=
2
).
run
(
suite
)
slapos/recipe/librecipe/execute.py
View file @
f9323891
...
...
@@ -43,7 +43,8 @@ def _wait_files_creation(file_list):
directories
[
dirname
][
event
.
name
]
=
True
finally
:
os
.
close
(
fd
)
if
isinstance
(
fd
,
int
):
os
.
close
(
fd
)
def
execute
(
args
):
"""Portable execution with process replacement"""
...
...
slapos/recipe/librecipe/inotify.py
View file @
f9323891
...
...
@@ -48,4 +48,5 @@ def subfiles(directory):
subfiles
=
[
file_
.
name
for
file_
in
inotifyx
.
get_events
(
watchfd
)]
finally
:
os
.
close
(
watchfd
)
if
isinstance
(
watchfd
,
int
):
os
.
close
(
watchfd
)
slapos/recipe/lockfile.py
View file @
f9323891
...
...
@@ -57,7 +57,9 @@ class LockFile(object):
except
IOError
:
# add_watch failed
pass
finally
:
os
.
close
(
inotify_fd
)
if
isinstance
(
inotify_fd
,
int
):
os
.
close
(
inotify_fd
)
self
.
__enter__
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment