Permalink
Browse files

add support for Linux, update to v0.5.0

  • Loading branch information...
1 parent 4ffc753 commit e8acd8cb0c40cf8929415b2c016d2ef3cebdb1b9 Tomasz Janczuk committed Jul 26, 2012
Showing with 160 additions and 10 deletions.
  1. +10 −3 README.md
  2. +6 −1 binding.gyp
  3. BIN lib/native/linux/x86/tripwire.node
  4. +3 −5 lib/tripwire.js
  5. +1 −1 package.json
  6. +140 −0 src/tripwire_linux.cc
View
13 README.md
@@ -3,7 +3,7 @@ Tripwire
Tripwire allows node.js applications to termiante execution of scripts that block the node.js event loop. For example, you can break out from infinite loops like `while(true)`. This functionality is useful if you are executing untrusted code within your node.js process.
-Tripwire contains a native extension of node.js and currently only supports Mac and Windows. I do take contributions.
+Tripwire contains a native extension of node.js and currently supports Windows, Mac, and Linux. I do take contributions.
Install with:
@@ -56,14 +56,14 @@ For more samples, see [here](https://github.com/tjanczuk/tripwire/tree/master/sa
There are a few mocha tests included that you can run with
```
-mocha -R List
+mocha -R list
```
#### Building
The native component is included in the repository and not built during `npm install tripwire`.
-You can rebuild the native component using [node-gyp](https://github.com/TooTallNate/node-gyp/). Currently the native component can be compiled on Mac and Windows only (I do take contributions).
+You can rebuild the native component using [node-gyp](https://github.com/TooTallNate/node-gyp/). Currently the native component can be compiled on Windows, Mac, and Linux (I do take contributions).
On Windows:
@@ -77,4 +77,11 @@ On Mac:
```
node-gyp configure build
cp build\Release\tripwire.node lib\native\darwin\x86\
+```
+
+On Linux:
+
+```
+node-gyp configure build
+cp build\Release\tripwire.node lib\native\linux\x86\
```
View
7 binding.gyp
@@ -15,7 +15,12 @@
'sources+': [
'src/tripwire_mac.cc'
]
- }]
+ }],
+ ['OS=="linux"', {
+ 'sources+': [
+ 'src/tripwire_linux.cc'
+ ]
+ }]
]
}
]
View
BIN lib/native/linux/x86/tripwire.node
Binary file not shown.
View
8 lib/tripwire.js
@@ -1,7 +1,5 @@
-if (process.platform === 'win32')
- module.exports = require('./native/windows/x86/tripwire');
-else if (process.platform === 'darwin')
- module.exports = require('./native/darwin/x86/tripwire')
+if (process.platform === 'win32' || process.platform === 'darwin' || process.platform === 'linux')
+ module.exports = require('./native/' + process.platform + '/x86/tripwire')
else
- throw new Error('The tripwire module is currently only suppored on Windows and Mac. I do take contributions. '
+ throw new Error('The tripwire module is currently only suppored on Windows, Mac, and Linux. I do take contributions. '
+ 'https://github.com/tjanczuk/tripwire');
View
2 package.json
@@ -5,7 +5,7 @@
"url": "http://tomasz.janczuk.org",
"twitter": "tjanczuk"
},
- "version": "0.5.0-pre",
+ "version": "0.5.0",
"description": "Break out from scripts blocking node.js event loop",
"tags" : ["event loop", "hang"],
"main": "./lib/tripwire.js",
View
140 src/tripwire_linux.cc
@@ -0,0 +1,140 @@
+#include <pthread.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <node.h>
+#include <v8.h>
+
+using namespace v8;
+
+pthread_t tripwireThread;
+pthread_mutex_t tripwireMutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t tripwireCondition = PTHREAD_COND_INITIALIZER;
+
+extern unsigned int tripwireThreshold;
+extern int terminated;
+
+void* tripwireWorker(void* data)
+{
+ int waitResult;
+ int skipTimeCapture = 0;
+ struct timespec timeout;
+ struct rusage start, end;
+
+ // This thread monitors the elapsed CPU utilization time of the node.js thread and forces V8 to terminate
+ // execution if it exceeds the preconfigured tripwireThreshold.
+
+ while (1)
+ {
+ // Unless the threshold validation logic requested to keep the current thread time utilization values,
+ // capture the current user mode and kernel mode CPU utilization time of the thread on which node.js executes
+ // application code.
+
+ if (skipTimeCapture)
+ skipTimeCapture = 0;
+ else
+ getrusage(RUSAGE_SELF, &start);
+
+ // Wait on the condition variable to be signalled. The variable will be signalled in one of two cases:
+ // 1. When the timeout value equal to tripwireThreshold elapses, or
+ // 2. When the variable is explicitly signalled from resetTripwire.
+ // A tripwireThreshold value of 0 indicates the tripwire mechanism is turned off, in which case
+ // an inifite wait is initiated on the variable (which will only be terminated with an explicit signal
+ // during subsequent call to resetThreashold).
+
+ pthread_mutex_lock(&tripwireMutex);
+ if (0 == tripwireThreshold)
+ {
+ waitResult = pthread_cond_wait(&tripwireCondition, &tripwireMutex);
+ }
+ else
+ {
+ clock_gettime(CLOCK_REALTIME, &timeout);
+ timeout.tv_sec += tripwireThreshold / 1000;
+ timeout.tv_nsec += (tripwireThreshold % 1000) * 1000000;
+ waitResult = pthread_cond_timedwait(&tripwireCondition, &tripwireMutex, &timeout);
+ }
+ pthread_mutex_unlock(&tripwireMutex);
+
+ if (ETIMEDOUT == waitResult)
+ {
+ // If the wait result on the variable is ETIMEDOUT, it means resetThreshold
+ // was not called in the tripwireThreshold period since the last call to resetThreshold. This indicates
+ // a possibility that the node.js thread is blocked.
+
+ // If tripwireThreshold is 0 at this point, however, it means a call to clearTripwire was made
+ // since the last call to resetThreshold. In this case we just skip tripwire enforcement and
+ // proceed to wait for a subsequent signal.
+
+ if (0 < tripwireThreshold)
+ {
+ // Take a snapshot of the current kernel and user mode CPU utilization time of the node.js thread
+ // to determine if the elapsed CPU utilization time exceeded the preconfigured tripwireThreshold.
+ // Despite the fact this code only ever executes after the auto reset event has already timeout out
+ // after the tripwireThreshold amount of time without hearing from the node.js thread, it need not
+ // necessarily mean that the node.js thread exceeded that execution time threshold. It might not
+ // have been running at all in that period, subject to OS scheduling.
+
+ getrusage(RUSAGE_SELF, &end);
+
+ // Process execution times are reported in seconds and microseconds. Convert to milliseconds.
+
+ unsigned int elapsedMs =
+ ((end.ru_utime.tv_sec - start.ru_utime.tv_sec) + (end.ru_stime.tv_sec - start.ru_stime.tv_sec))
+ * 1000
+ + ((end.ru_utime.tv_usec - start.ru_utime.tv_usec) + (end.ru_stime.tv_usec - start.ru_stime.tv_usec))
+ / 1000;
+
+ // If the actual CPU execution time of the node.js thread exceeded the threshold, terminate
+ // the V8 process. Otherwise wait again while maintaining the current snapshot of the initial
+ // time utilization. This mechanism results in termination of a runaway thread some time in the
+ // (tripwireThreshold, 2 * tripwireThreshold) range of CPU utilization.
+
+ if (elapsedMs >= tripwireThreshold)
+ {
+ terminated = 1;
+ V8::TerminateExecution();
+ }
+ else
+ {
+ skipTimeCapture = 1;
+ }
+ }
+ }
+ }
+
+ pthread_exit(NULL);
+}
+
+Handle<Value> resetTripwireCore()
+{
+ HandleScope scope;
+
+ if (0 == tripwireThread)
+ {
+ // This is the first call to resetTripwire. Perform lazy initialization.
+ // Create the worker thread.
+
+ if (0 != pthread_create(&tripwireThread, NULL, tripwireWorker, NULL))
+ {
+ return ThrowException(Exception::Error(String::New("Unable to initialize a tripwire thread.")));
+ }
+ }
+ else
+ {
+ // Signal the already existing worker thread using the condition variable.
+ // This will cause the worker thread to
+ // reset the elapsed time timer and pick up the new tripwireThreshold value.
+
+ pthread_mutex_lock(&tripwireMutex);
+ pthread_cond_signal(&tripwireCondition);
+ pthread_mutex_unlock(&tripwireMutex);
+ }
+
+ return Undefined();
+}
+
+void initCore()
+{
+ tripwireThread = 0;
+}

0 comments on commit e8acd8c

Please sign in to comment.