mirror of https://gitee.com/bigwinds/arangodb
fixed merge conflicts
This commit is contained in:
commit
503e0ff8fe
|
@ -4,6 +4,7 @@ mr-*.h
|
|||
.dirstamp
|
||||
*.o
|
||||
*.a
|
||||
*~
|
||||
|
||||
.libev-build-64
|
||||
.v8-build-64
|
||||
|
|
|
@ -135,6 +135,11 @@ v1.2.alpha (XXXX-XX-XX)
|
|||
v1.1.2 (XXXX-XX-XX)
|
||||
-------------------
|
||||
|
||||
* backported issue #300: Extend arangoImp to Allow importing resultset-like
|
||||
(list of documents) formatted files
|
||||
|
||||
* fixed issue #332: arangoimp --use-ids parameter seems to have no impact
|
||||
|
||||
* fixed issue #336: Collections REST API docs
|
||||
|
||||
* fixed issue #335: mmap errors due to wrong memory address calculation
|
||||
|
|
|
@ -7,9 +7,8 @@ Installing ArangoDB {#Installing}
|
|||
Linux {#InstallingLinux}
|
||||
========================
|
||||
|
||||
You can find binary packages for various Linux distributions here:
|
||||
|
||||
@EXTREF{http://www.arangodb.org/download/,http://www.arangodb.org/download/}
|
||||
You can find binary packages for various Linux distributions
|
||||
@EXTREF{http://www.arangodb.org/download/,here}.
|
||||
|
||||
We provide packages for
|
||||
|
||||
|
|
|
@ -161,6 +161,7 @@ Doxygen/xml/%.md: Doxygen/xml/%.xml
|
|||
doxygen-toc:
|
||||
python @top_srcdir@/Documentation/Scripts/generateTOC.py $(DOXYGEN_TOC) >> Doxygen/toc.doxy.tmp
|
||||
cmp -s Doxygen/toc.doxy Doxygen/toc.doxy.tmp || mv Doxygen/toc.doxy.tmp Doxygen/toc.doxy
|
||||
@rm -f Doxygen/toc.doxy.tmp
|
||||
|
||||
Doxygen/toc.doxy: doxygen-toc
|
||||
|
||||
|
|
|
@ -0,0 +1,647 @@
|
|||
Arango Actions {#UserManualActions}
|
||||
===================================
|
||||
|
||||
@NAVIGATE_UserManualActions
|
||||
@EMBEDTOC{UserManualActionsTOC}
|
||||
|
||||
Please note, that user Actions in ArangoDB are still preliminary and details
|
||||
are subject to change.
|
||||
|
||||
Introduction to User Actions {#UserManualActionsIntro}
|
||||
======================================================
|
||||
|
||||
In some ways the communication layer of the ArangoDB server behaves like a Web
|
||||
server. Unlike a Web server, it normally responds to HTTP requests by delivering
|
||||
JSON objects. Remember, documents in the database are just JSON objects. So,
|
||||
most of the time the HTTP response will contain a JSON document from the
|
||||
database as body. You can extract the documents stored in the database using
|
||||
HTTP `GET`. You can store documents using HTTP `POST`.
|
||||
|
||||
However, there is something more. You can write small sniplets - so called
|
||||
actions - to extend the database. The idea of actions is that sometimes it is
|
||||
better to store parts of the business logic within AnrangoDB.
|
||||
|
||||
The simplest example is the age of a person. Assume you store information about
|
||||
people in your database. It is an anti-pattern to store the age, because it
|
||||
changes every now and then. Therefore, you normally store the birthday and let
|
||||
the client decide what to do with it. However, if you have many different
|
||||
clients, it might be easier to enrich the person document with the age using
|
||||
actions once on the server side.
|
||||
|
||||
Or, for instance, if you want to apply some statistics to large data-sets and
|
||||
you cannot easily express this as query. You can define a action instead of
|
||||
transferring the whole data to the client and do the computation on the client.
|
||||
|
||||
Actions are also useful if you want to restrict and filter data according to
|
||||
some complex permission system.
|
||||
|
||||
The ArangoDB server can deliver all kinds of information, JSON being only one
|
||||
possible format. You can also generate HTML or images. However, a Web server is
|
||||
normally better suited for the task as it also implements various caching
|
||||
strategies, language selection, compression and so on. Having said that, there
|
||||
are still situations where it might be suitable to use the ArangoDB to deliver
|
||||
HTML pages - static or dynamic. An simple example is the built-in administration
|
||||
interface. You can access it using any modern browser and there is no need for a
|
||||
separate Apache or IIS.
|
||||
|
||||
The following sections will explain actions within ArangoDB and show how to
|
||||
define them. The examples start with delivering static HTML pages - even if this
|
||||
is not the primary use-case for actions. The later sections will then show you
|
||||
how to code some pieces of your business logic and return JSON objects.
|
||||
|
||||
The interface is loosely modelled after the JavaScript classes for HTTP request
|
||||
and responses found in node.js and the middleware/routing aspects of connect.js
|
||||
and express.js.
|
||||
|
||||
Note that unlike node.js, ArangoDB is multi-threaded and there is no easy way to
|
||||
share state between queries inside the JavaScript engine. If such state
|
||||
information is required, you need to use the database itself.
|
||||
|
||||
A Hello World Example {#UserManualActionsHelloWorld}
|
||||
====================================================
|
||||
|
||||
The client API or browser sends a HTTP request to the ArangoDB server and the
|
||||
server returns a HTTP response to the client. A HTTP request consists of a
|
||||
method, normally `GET` or `POST` when using a browser, and a request path like
|
||||
`/hello/world`. For a real Web server there are a zillion of other thing to
|
||||
consider, we will ignore this for the moment. The HTTP response contains a
|
||||
content type, describing how to interpret the returned data, and the data
|
||||
itself.
|
||||
|
||||
In the following example, we want to define an action in ArangoDB, so that the
|
||||
server returns the HTML document
|
||||
|
||||
<html>
|
||||
<body>
|
||||
Hello World
|
||||
</body>
|
||||
</html>
|
||||
|
||||
if asked `GET /hello/world`.
|
||||
|
||||
The server needs to know what function to call or what document to deliver if it
|
||||
receives a request. This is called routing. All the routing information of
|
||||
ArangoDB is stored in a collection `_routing`. Each entry in this collections
|
||||
describes how to deal with a particular request path.
|
||||
|
||||
For the above example, add the following document to the @{_routing} collection:
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: { match: "/hello/world" },
|
||||
........> content: {
|
||||
........> contentType: "text/html",
|
||||
........> body: "<html><body>Hello World</body></html>" }});
|
||||
|
||||
In order to activate the new routing, you must either restart the server or call
|
||||
the internal reload function.
|
||||
|
||||
arangosh> require("internal").reloadRouting()
|
||||
|
||||
Now use the browser and access
|
||||
|
||||
http://localhost:8529/hello/world
|
||||
|
||||
You should see the `Hello World` in our browser.
|
||||
|
||||
Matching an URL {#UserManualActionsMatches}
|
||||
===========================================
|
||||
|
||||
There are a lot of options for the `url` attribute. If you define different
|
||||
routing for the same path, then the following simple rule is applied in order to
|
||||
determine which match wins: If there are two matches, then the more specific
|
||||
wins. I. e, if there is a wildcard match and an exact match, the exact match is
|
||||
prefered. If there is a short and a long match, the longer match wins.
|
||||
|
||||
Exact Match {#UserManualActionsMatchesExact}
|
||||
--------------------------------------------
|
||||
|
||||
If the definition is
|
||||
|
||||
{ url: { match: "/hello/world" } }
|
||||
|
||||
then the match must be exact. Only the request for `/hello/world` will match,
|
||||
everything else, e. g. `/hello/world/my` or `/hello/world2`, will not match.
|
||||
|
||||
The following definition is a short-cut for an exact match.
|
||||
|
||||
{ url: "/hello/world" }
|
||||
|
||||
Prefix Match {#UserManualActionsMatchesPrefix}
|
||||
----------------------------------------------
|
||||
|
||||
If the definition is
|
||||
|
||||
{ url: { match: "/hello/world/*" } }
|
||||
|
||||
then the match can be a prefix match. The requests for `/hello/world`,
|
||||
`/hello/world/my`, and `/hello/world/how/are/you` will all match. However
|
||||
`/hello/world2` does not match. Prefix matches within an URL part,
|
||||
i. e. `/hello/world*`, are not allowed. The wildcard must occur at the end,
|
||||
i. e.
|
||||
|
||||
/hello/*/world
|
||||
|
||||
is also disallowed.
|
||||
|
||||
If you define two routes
|
||||
|
||||
{ url: { match: "/hello/world/*" } }
|
||||
{ url: { match: "/hello/world/emil" } }
|
||||
|
||||
then the second route will be used for `/hello/world/emil` because it is more
|
||||
specific.
|
||||
|
||||
Parameterized Match {#UserManualActionsMatchesParameterized}
|
||||
------------------------------------------------------------
|
||||
|
||||
A parameterized match is similar to a prefix match, but the parameters are also
|
||||
allowed inside the URL path.
|
||||
|
||||
If the definition is
|
||||
|
||||
{ url: { match: "/hello/:name/world" } }
|
||||
|
||||
then the URL must have three parts, the first part being `hello` and the third
|
||||
part `world`. For example, `/hello/emil/world` will match, while
|
||||
`/hello/emil/meyer/world` will not.
|
||||
|
||||
Constraint Match {#UserManualActionsMatchesConstraint}
|
||||
------------------------------------------------------
|
||||
|
||||
A constraint match is similar to a parameterized match, but the parameters can
|
||||
carry constraints.
|
||||
|
||||
If the definition is
|
||||
|
||||
{ url: { match: "/hello/:name/world", constraint: { name: "/[a-z]+/" } }
|
||||
|
||||
then the URL must have three parts, the first part being `hello` and the third
|
||||
part `world`. The second part must be all lowercase.
|
||||
|
||||
It is possible to use more then one constraint for the same URL part.
|
||||
|
||||
{ url: { match: "/hello/:name|:id/world",
|
||||
constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }
|
||||
|
||||
Optional Match {#UserManualActionsMatchesOptional}
|
||||
--------------------------------------------------
|
||||
|
||||
An optional match is similar to a parameterized match, but the last parameter is
|
||||
optional.
|
||||
|
||||
If the definition is
|
||||
|
||||
{ url: { match: "/hello/:name?", constraint: { name: "/[a-z]+/" } }
|
||||
|
||||
then the URL `/hello` and `/hello/emil` will match.
|
||||
|
||||
If the definitions are
|
||||
|
||||
{ url: { match: "/hello/world" } }
|
||||
{ url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } }
|
||||
{ url: { match: "/hello/*" } }
|
||||
|
||||
then the URL `/hello/world` will be matched by the first route, because it is
|
||||
the most specific. The URL `/hello/you` will be matched by the second route,
|
||||
because it is more specific than the prefix match.
|
||||
|
||||
Method Restriction {#UserManualActionsMatchesMethod}
|
||||
----------------------------------------------------
|
||||
|
||||
You can restrict the match to specific methods.
|
||||
|
||||
If the definition is
|
||||
|
||||
{ url: { match: "/hello/world", methods: [ "post", "put" ] }
|
||||
|
||||
then only `POST` and `PUT` requests will match.
|
||||
|
||||
More on Matching {#UserManualActionsMatching}
|
||||
---------------------------------------------
|
||||
|
||||
Remember that the more specific match wins.
|
||||
|
||||
- A match without parameter or wildcard is more specific than a match with
|
||||
parameters or wildcard.
|
||||
- A match with parameter is more specific than a match with a wildcard.
|
||||
- If there is more than one parameter, specificity is applied from left to
|
||||
right.
|
||||
|
||||
Consider the following definitions
|
||||
|
||||
(1) { url: { match: "/hello/world" } }
|
||||
(2) { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } }
|
||||
(3) { url: { match: "/:something/world" }
|
||||
(4) { url: { match: "/hello/*" } }
|
||||
|
||||
Then
|
||||
|
||||
- `/hello/world` is match by (1)
|
||||
- `/hello/emil` is match by (2)
|
||||
- `/your/world` is match by (3)
|
||||
- `/hello/you` is match by (4)
|
||||
|
||||
You can write the following document into the `_routing` collection
|
||||
to test the above examples.
|
||||
|
||||
{
|
||||
routes: [
|
||||
{ url: { match: "/hello/world" }, content: "route 1" },
|
||||
{ url: { match: "/hello/:name|:id", constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }, content: "route 2" },
|
||||
{ url: { match: "/:something/world" }, content: "route 3" },
|
||||
{ url: { match: "/hello/*" }, content: "route 4" },
|
||||
]
|
||||
}
|
||||
|
||||
A Hello World Example for JSON {#UserManualActionsHelloJson}
|
||||
============================================================
|
||||
|
||||
If you change the example slightly, then a JSON object will be delivered.
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/hello/json",
|
||||
........> content: {
|
||||
........> contentType: "application/json",
|
||||
........> body: "{ \"hello\" : \"world\" }" }});
|
||||
arangosh> require("internal").reloadRouting()
|
||||
|
||||
Again check with your browser
|
||||
|
||||
http://localhost:8529/hello/json
|
||||
|
||||
Depending on your browser and installed add-ons you will either see the JSON
|
||||
object or a download dialog. If your browser wants to open an external
|
||||
application to display the JSON object, you can change the `contentType` to
|
||||
`"text/plain"` for the example. This makes it easier to check the example using
|
||||
a browser. Or use `curl` to access the server.
|
||||
|
||||
bash> curl "http://127.0.0.1:8529/hello/json" && echo
|
||||
{ "hello" : "world" }
|
||||
|
||||
Delivering Content {#UserManualActionsContent}
|
||||
==============================================
|
||||
|
||||
There are a lot of different ways on how to deliver content. We have already
|
||||
seen the simplest one, where static content is delivered. The fun, however,
|
||||
starts when delivering dynamic content.
|
||||
|
||||
Static Content {#UserManualActionsContentStatic}
|
||||
------------------------------------------------
|
||||
|
||||
You can specify a body and a content-type.
|
||||
|
||||
{ content: {
|
||||
contentType: "text/html",
|
||||
body: "<html><body>Hallo World</body></html>"
|
||||
}
|
||||
}
|
||||
|
||||
If the content type is `text/plain` then you can use the short-cut
|
||||
|
||||
{ content: "Hallo World" }
|
||||
|
||||
A Simple Action {#UserManualActionsContentAction}
|
||||
=================================================
|
||||
|
||||
The simplest dynamic action is:
|
||||
|
||||
{ action: { controller: "org/arangodb/actions", do: "echoRequest" } }
|
||||
|
||||
It is not possible to store functions directly in the routing table, but you can
|
||||
call functions defined in modules. In the above example the function can be
|
||||
accessed from JavaScript as:
|
||||
|
||||
require("org/arangodb/actions").echoRequest
|
||||
|
||||
The function `echoRequest` is pre-defined. It takes the request objects and
|
||||
echos it in the response.
|
||||
|
||||
The signature of such a function must be
|
||||
|
||||
function (req, res, options, next)
|
||||
|
||||
For example
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/hello/echo",
|
||||
........> action: { controller: "org/arangodb/actions", do: "echoRequest" } });
|
||||
|
||||
Reload the routing and check
|
||||
|
||||
http://127.0.0.1:8529/hello/echo
|
||||
|
||||
You should see something like
|
||||
|
||||
{
|
||||
"request": {
|
||||
"path": "/hello/echo",
|
||||
"headers": {
|
||||
"accept-encoding": "gzip, deflate",
|
||||
"accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3",
|
||||
"connection": "keep-alive",
|
||||
"content-length": "0",
|
||||
"host": "localhost:8529",
|
||||
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0"
|
||||
},
|
||||
"requestType": "GET",
|
||||
"parameters": { }
|
||||
},
|
||||
"options": { }
|
||||
}
|
||||
|
||||
The request might contain `path`, `prefix`, `suffix`, and `urlParameters`
|
||||
attributes. `path` is the complete path as supplied by the user and always
|
||||
available. If a prefix was matched, then this prefix is stored in the attribute
|
||||
`prefix` and the remaining URL parts are stored as an array in `suffix`. If one
|
||||
or more parameters were matched, then the parameter values are stored in
|
||||
`urlParameters`.
|
||||
|
||||
For example, if the url description is
|
||||
|
||||
{ url: { match: "/hello/:name/:action" } }
|
||||
|
||||
and you request the path `/hello/emil/jump`, then the request object
|
||||
will contain the following attribute
|
||||
|
||||
urlParameters: { name: "emil", action: "jump" } }
|
||||
|
||||
Action Controller {#UserManualActionsContentController}
|
||||
-------------------------------------------------------
|
||||
|
||||
As an alternative to the simple action, you can use controllers. A
|
||||
controller is a module, defines the function `get`, `put`,
|
||||
`post`, `delete`, `head`, `patch`. If a request of
|
||||
the corresponding type is matched, the function will be called.
|
||||
|
||||
For example
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/hello/echo",
|
||||
........> action: { controller: "org/arangodb/actions/echoController" } });
|
||||
|
||||
Prefix Action Controller {#UserManualActionsContentPrefix}
|
||||
----------------------------------------------------------
|
||||
|
||||
The controller is selected when the definition is read. There is a
|
||||
more flexible, but slower and maybe insecure variant, the prefix
|
||||
controller.
|
||||
|
||||
Assume that the url is a prefix match
|
||||
|
||||
{ url: { match: /hello/*" } }
|
||||
|
||||
You can use
|
||||
|
||||
{ action: { prefixController: "org/arangodb/actions" } }
|
||||
|
||||
to define a prefix controller. If the URL `/hello/echoController` is
|
||||
given, then the module `org/arangodb/actions/echoController` is used.
|
||||
|
||||
If you use an prefix controller, you should make certain that no unwanted
|
||||
actions are available under the prefix.
|
||||
|
||||
The definition
|
||||
|
||||
{ action: "org/arangodb/actions" }
|
||||
|
||||
is a short-cut for a prefix controller definition.
|
||||
|
||||
Requests and Responses {#UserManualActionsReqRes}
|
||||
=================================================
|
||||
|
||||
The controller must define handler functions which take a request object and
|
||||
fill the response object.
|
||||
|
||||
A very simple example is the function `echoRequest` defined in
|
||||
the module `org/arangodb/actions`.
|
||||
|
||||
function (req, res, options, next) {
|
||||
var result;
|
||||
|
||||
result = { request: req, options: options };
|
||||
|
||||
res.responseCode = exports.HTTP_OK;
|
||||
res.contentType = "application/json";
|
||||
res.body = JSON.stringify(result);
|
||||
}
|
||||
|
||||
Install it as
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/echo",
|
||||
........> action: { controller: "org/arangodb/actions", do: "echoRequest" } });
|
||||
|
||||
Reload the routing and check
|
||||
|
||||
http://127.0.0.1:8529/hello/echo
|
||||
|
||||
You should see something like
|
||||
|
||||
{
|
||||
"request": {
|
||||
"prefix": "/hello/echo",
|
||||
"suffix": [
|
||||
"hello",
|
||||
"echo"
|
||||
],
|
||||
"path": "/hello/echo",
|
||||
"headers": {
|
||||
"accept-encoding": "gzip, deflate",
|
||||
"accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3",
|
||||
"connection": "keep-alive",
|
||||
"content-length": "0",
|
||||
"host": "localhost:8529",
|
||||
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0"
|
||||
},
|
||||
"requestType": "GET",
|
||||
"parameters": { }
|
||||
},
|
||||
"options": { }
|
||||
}
|
||||
|
||||
You may also pass options to the called function:
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/echo",
|
||||
........> action: {
|
||||
........> controller: "org/arangodb/actions",
|
||||
........> do: "echoRequest",
|
||||
........> options: { "Hallo": "World" } } });
|
||||
|
||||
You should now see the options in the result.
|
||||
|
||||
{
|
||||
"request": {
|
||||
...
|
||||
},
|
||||
"options": {
|
||||
"Hallo": "World"
|
||||
}
|
||||
}
|
||||
|
||||
Modifying Request and Response {#UserManualActionsModify}
|
||||
=========================================================
|
||||
|
||||
As we've seen in the previous examples, actions get called with the request and
|
||||
response objects (named `req` and `res` in the examples) passed as parameters to
|
||||
their handler functions.
|
||||
|
||||
The `req` object contains the incoming HTTP request, which might or might not
|
||||
have been modified by a previous action (if actions were chained).
|
||||
|
||||
A handler can modify the request object in place if desired. This might be
|
||||
useful when writing middleware (see below) that is used to intercept incoming
|
||||
requests, modify them and pass them to the actual handlers.
|
||||
|
||||
While modifying the request object might not be that relevant for non-middleware
|
||||
actions, modifying the response object definitely is. Modifying the response
|
||||
object is an action's only way to return data to the caller of the action.
|
||||
|
||||
We've already seen how to set the HTTP status code, the content type, and the
|
||||
result body. The `res` object has the following properties for these:
|
||||
|
||||
- contentType: MIME type of the body as defined in the HTTP standard (e.g.
|
||||
`text/html`, `text/plain`, `application/json`, ...)
|
||||
- responsecode: the HTTP status code of the response as defined in the HTTP
|
||||
standard. Common values for actions that succeed are `200` or `201`.
|
||||
Please refer to the HTTP standard for more information.
|
||||
- body: the actual response data
|
||||
|
||||
To set or modify arbitrary headers of the response object, the `headers`
|
||||
property can be used. For example, to add a user-defined header to the response,
|
||||
the following code will do:
|
||||
|
||||
res.headers = res.headers || { }; // headers might or might not be present
|
||||
res.headers['X-Test'] = 'someValue'; // set header X-Test to "someValue"
|
||||
|
||||
This will set the additional HTTP header `X-Test` to value `someValue`. Other
|
||||
headers can be set as well. Note that ArangoDB might change the case of the
|
||||
header names to lower case when assembling the overall response that is sent to
|
||||
the caller.
|
||||
|
||||
It is not necessary to explicitly set a `Content-Length` header for the response
|
||||
as ArangoDB will calculate the content length automatically and add this header
|
||||
itself. ArangoDB might also add a `Connection` header itself to handle HTTP
|
||||
keep-alive.
|
||||
|
||||
ArangoDB also supports automatic transformation of the body data to another
|
||||
format. Currently, the only supported transformations are base64-encoding and
|
||||
base64-decoding. Using the transformations, an action can create a base64
|
||||
encoded body and still let ArangoDB send the non-encoded version, for example:
|
||||
|
||||
res.body = 'VGhpcyBpcyBhIHRlc3Q=';
|
||||
res.transformations = res.transformations || [ ]; // initialise
|
||||
res.transformations.push('base64decode'); // will base64 decode the response body
|
||||
|
||||
When ArangoDB processes the response, it will base64-decode what's in `res.body`
|
||||
and set the HTTP header `Content-Encoding: binary`. The opposite can be achieved
|
||||
with the `base64encode` transformation: ArangoDB will then automatically
|
||||
base64-encode the body and set a `Content-Encoding: base64` HTTP header.
|
||||
|
||||
Writing dynamic action handlers {#UserManualActionsHandlers}
|
||||
============================================================
|
||||
|
||||
To write your own dynamic action handlers, you must put them into modules.
|
||||
|
||||
Modules are a means of organising action handlers and making them loadable under
|
||||
specific names.
|
||||
|
||||
To start, we'll define a simple action handler in a module `/own/test`:
|
||||
|
||||
arangosh> db._modules.save({
|
||||
........> path: "/own/test",
|
||||
........> content: "exports.do = function(req, res, options, next) { res.body = 'test'; res.responseCode = 200; res.contentType = 'text/html'; };",
|
||||
........> autoload: true });
|
||||
|
||||
This does nothing but register a do action handler in a module `/own/test`. The
|
||||
action handler is not yet callable, but must be mapped to a route first. To map
|
||||
the action to the route `/ourtest`, execute the following command:
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/ourtest",
|
||||
........> action: { controller: "/own/test" } });
|
||||
|
||||
In order to see the module in action, you must either restart the server or call
|
||||
the internal reload function.
|
||||
|
||||
arangosh> require("internal").reloadRouting()
|
||||
|
||||
Now use the browser and access
|
||||
|
||||
http://localhost:8529/ourtest
|
||||
|
||||
You will see that the module's do function has been executed.
|
||||
|
||||
Advanced Usages {#UserManualActionsAdvanced}
|
||||
============================================
|
||||
|
||||
For detailed information see the reference manual.
|
||||
|
||||
Redirects {#UserManualActionsAdvancedRedirects}
|
||||
-----------------------------------------------
|
||||
|
||||
Use the following for a permanent redirect:
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> url: "/",
|
||||
........> action: {
|
||||
........> controller: "org/arangodb/actions",
|
||||
........> do: "redirectRequest",
|
||||
........> options: {
|
||||
........> permanently: true,
|
||||
........> destination: "http://somewhere.else/" } } });
|
||||
|
||||
Routing Bundles {#UserManualActionsAdvancedBundles}
|
||||
---------------------------------------------------
|
||||
|
||||
Instead of adding all routes for package separately, you can
|
||||
specify a bundle.
|
||||
|
||||
{
|
||||
routes: [
|
||||
{ url: "/url1", content: "..." },
|
||||
{ url: "/url2", content: "..." },
|
||||
{ url: "/url3", content: "..." },
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
The advantage is, that you can put all your routes into one document
|
||||
and use a common prefix.
|
||||
|
||||
{
|
||||
urlPrefix: "/test",
|
||||
|
||||
routes: [
|
||||
{ url: "/url1", content: "..." },
|
||||
{ url: "/url2", content: "..." },
|
||||
{ url: "/url3", content: "..." },
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
will define the URL `/test/url1`, `/test/url2`, and `/test/url3`.
|
||||
|
||||
Writing Middleware {#UserManualActionsAdvancedMiddleware}
|
||||
---------------------------------------------------------
|
||||
|
||||
Assume, you want to log every request. In this case you can easily define an
|
||||
action for the whole url-space `/`. This action simply logs the requests, calls
|
||||
the next in line, and logs the response.
|
||||
|
||||
exports.logRequest = function (req, res, options, next) {
|
||||
console.log("received request: %s", JSON.stringify(req));
|
||||
next();
|
||||
console.log("produced response: %s", JSON.stringify(res));
|
||||
};
|
||||
|
||||
This function is available as `org/arangodb/actions/logRequest`. You need to
|
||||
tell ArangoDB that it is should use a prefix match and that the shortest match
|
||||
should win in this case:
|
||||
|
||||
arangosh> db._routing.save({
|
||||
........> middleware: [
|
||||
........> { url: { match: "/*" }, action: { controller: "org/arangodb/actions", do: "logRequest" } }
|
||||
........> ]
|
||||
........> });
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
TOC {#UserManualActionsTOC}
|
||||
===========================
|
||||
|
||||
- @ref UserManualActions
|
||||
- @ref UserManualActionsIntro
|
||||
- @ref UserManualActionsHelloWorld
|
||||
- @ref UserManualActionsMatches
|
||||
- @ref UserManualActionsMatchesExact
|
||||
- @ref UserManualActionsMatchesPrefix
|
||||
- @ref UserManualActionsMatchesParameterized
|
||||
- @ref UserManualActionsMatchesConstraint
|
||||
- @ref UserManualActionsMatchesOptional
|
||||
- @ref UserManualActionsMatchesMethod
|
||||
- @ref UserManualActionsMatching
|
||||
- @ref UserManualActionsHelloJson
|
||||
- @ref UserManualActionsContent
|
||||
- @ref UserManualActionsContentStatic
|
||||
- @ref UserManualActionsContentAction
|
||||
- @ref UserManualActionsContentController
|
||||
- @ref UserManualActionsContentPrefix
|
||||
- @ref UserManualActionsReqRes
|
||||
- @ref UserManualActionsModify
|
||||
- @ref UserManualActionsHandlers
|
||||
- @ref UserManualActionsAdvanced
|
||||
- @ref UserManualActionsAdvancedRedirects
|
||||
- @ref UserManualActionsAdvancedBundles
|
||||
- @ref UserManualActionsAdvancedMiddleware
|
|
@ -8,7 +8,7 @@
|
|||
### @brief source to build before compile
|
||||
################################################################################
|
||||
|
||||
BUILT_SOURCES = build.h
|
||||
BUILT_SOURCES = build_posix.h
|
||||
|
||||
################################################################################
|
||||
### @brief man pages to install
|
||||
|
@ -253,8 +253,8 @@ install-data-local:
|
|||
### @brief version number
|
||||
################################################################################
|
||||
|
||||
build.h: configure.ac
|
||||
@echo '#define TRIAGENS_VERSION "@PACKAGE_VERSION@"' > build.h
|
||||
build_posix.h: configure.ac
|
||||
@echo '#define TRIAGENS_VERSION "@PACKAGE_VERSION@"' > build_posix.h
|
||||
|
||||
################################################################################
|
||||
### @brief local modifications
|
||||
|
@ -323,7 +323,7 @@ clean-local:
|
|||
.PHONY: built-sources
|
||||
|
||||
built-sources: \
|
||||
build.h \
|
||||
build_posix.h \
|
||||
@top_srcdir@/js/common/bootstrap/errors.js \
|
||||
$(JAVASCRIPT_HEADER)
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ RestActionHandler::RestActionHandler (HttpRequest* request, action_options_t* da
|
|||
_action(0),
|
||||
_queue(),
|
||||
_allowed(false) {
|
||||
|
||||
|
||||
_action = TRI_LookupActionVocBase(request);
|
||||
|
||||
// check if the action is allowed
|
||||
|
|
|
@ -158,6 +158,7 @@ TRI_action_t* TRI_LookupActionVocBase (triagens::rest::HttpRequest* request) {
|
|||
string name = StringUtils::join(suffix, '/');
|
||||
map<string, TRI_action_t*>::iterator i = Actions.find(name);
|
||||
|
||||
|
||||
if (i != Actions.end()) {
|
||||
return i->second;
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ static inline bool AddRow (TRI_aql_explain_t* const explain, TRI_json_t* value)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline TRI_json_t* GetRowProtoType (TRI_aql_explain_t* const explain,
|
||||
const TRI_aql_node_type_e const type) {
|
||||
const TRI_aql_node_type_e type) {
|
||||
TRI_json_t* row;
|
||||
|
||||
row = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -290,7 +290,7 @@ void TRI_FreeIndexAql (TRI_aql_index_t* const idx) {
|
|||
TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
||||
const TRI_vector_pointer_t* const availableIndexes,
|
||||
const char* const collectionName,
|
||||
const TRI_vector_pointer_t* const candidates) {
|
||||
const TRI_vector_pointer_t* candidates) {
|
||||
TRI_aql_index_t* picked = NULL;
|
||||
TRI_vector_pointer_t matches;
|
||||
size_t i, n;
|
||||
|
|
|
@ -369,7 +369,7 @@ void TRI_FreeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_WalkStatementsAql (TRI_aql_statement_walker_t* const walker,
|
||||
TRI_aql_statement_list_t* const list) {
|
||||
TRI_aql_statement_list_t* list) {
|
||||
assert(walker);
|
||||
assert(list);
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ int BitarrayIndex_new(BitarrayIndex** baIndex,
|
|||
|
||||
|
||||
// ...........................................................................
|
||||
// Sime simple checks
|
||||
// Some simple checks
|
||||
// ...........................................................................
|
||||
|
||||
if (baIndex == NULL) {
|
||||
|
|
|
@ -100,785 +100,6 @@
|
|||
/// JSON output format.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- USER MANUAL ACTIONS
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @page UserManualActionsTOC
|
||||
///
|
||||
/// <ul>
|
||||
/// <li>@ref UserManualActions
|
||||
/// <ul>
|
||||
/// <li>@ref UserManualActionsIntro</li>
|
||||
/// <li>@ref UserManualActionsHelloWorld</li>
|
||||
/// <li>@ref UserManualActionsMatches
|
||||
/// <ul>
|
||||
/// <li>@ref UserManualActionsMatchesExact</li>
|
||||
/// <li>@ref UserManualActionsMatchesPrefix</li>
|
||||
/// <li>@ref UserManualActionsMatchesParameterized</li>
|
||||
/// <li>@ref UserManualActionsMatchesConstraint</li>
|
||||
/// <li>@ref UserManualActionsMatchesOptional</li>
|
||||
/// <li>@ref UserManualActionsMatchesMethod</li>
|
||||
/// <li>@ref UserManualActionsMatching</li>
|
||||
/// </ul>
|
||||
/// </li>
|
||||
/// <li>@ref UserManualActionsHelloJson</li>
|
||||
/// <li>@ref UserManualActionsContent
|
||||
/// <ul>
|
||||
/// <li>@ref UserManualActionsContentStatic</li>
|
||||
/// <li>@ref UserManualActionsContentAction</li>
|
||||
/// <li>@ref UserManualActionsContentController</li>
|
||||
/// <li>@ref UserManualActionsContentPrefix</li>
|
||||
/// </ul>
|
||||
/// </li>
|
||||
/// <li>@ref UserManualActionsReqRes</li>
|
||||
/// <li>@ref UserManualActionsModify</li>
|
||||
/// <li>@ref UserManualActionsHandlers</li>
|
||||
/// <li>@ref UserManualActionsAdvanced
|
||||
/// <ul>
|
||||
/// <li>@ref UserManualActionsAdvancedRedirects</li>
|
||||
/// <li>@ref UserManualActionsAdvancedBundles</li>
|
||||
/// <li>@ref UserManualActionsAdvancedMiddleware</li>
|
||||
/// </ul>
|
||||
/// </li>
|
||||
/// </ul>
|
||||
/// </li>
|
||||
/// </ul>
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @page UserManualActions Arango Actions
|
||||
///
|
||||
/// @NAVIGATE_UserManualActions
|
||||
///
|
||||
/// Please note, that user Actions in ArangoDB are still preliminary and details
|
||||
/// are subject to change.
|
||||
///
|
||||
/// @EMBEDTOC{UserManualActionsTOC}
|
||||
///
|
||||
/// @section UserManualActionsIntro Introduction to User Actions
|
||||
////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// In some ways the communication layer of the ArangoDB server behaves like a
|
||||
/// Web server. Unlike a Web server, it normally responds to HTTP requests by
|
||||
/// delivering JSON objects. Remember, documents in the database are just JSON
|
||||
/// objects. So, most of the time the HTTP response will contain a JSON document
|
||||
/// from the database as body. You can extract the documents stored in the
|
||||
/// database using HTTP @LIT{GET}. You can store documents using HTTP
|
||||
/// @LIT{POST}.
|
||||
///
|
||||
/// However, there is something more. You can write small sniplets - so called
|
||||
/// actions - to extend the database. The idea of actions is that sometimes it
|
||||
/// is better to store parts of the business logic within AnrangoDB.
|
||||
///
|
||||
/// The simplest example is the age of a person. Assume you store information
|
||||
/// about people in your database. It is an anti-pattern to store the age,
|
||||
/// because it changes every now and then. Therefore, you normally store the
|
||||
/// birthday and let the client decide what to do with it. However, if you have
|
||||
/// many different clients, it might be easier to enrich the person document
|
||||
/// with the age using actions once on the server side.
|
||||
///
|
||||
/// Or, for instance, if you want to apply some statistics to large data-sets
|
||||
/// and you cannot easily express this as query. You can define a action instead
|
||||
/// of transferring the whole data to the client and do the computation on the
|
||||
/// client.
|
||||
///
|
||||
/// Actions are also useful if you want to restrict and filter data according to
|
||||
/// some complex permission system.
|
||||
///
|
||||
/// The ArangoDB server can deliver all kinds of information, JSON being only
|
||||
/// one possible format. You can also generate HTML or images. However, a Web
|
||||
/// server is normally better suited for the task as it also implements various
|
||||
/// caching strategies, language selection, compression and so on. Having said
|
||||
/// that, there are still situations where it might be suitable to use the
|
||||
/// ArangoDB to deliver HTML pages - static or dynamic. An simple example is the
|
||||
/// built-in administration interface. You can access it using any modern
|
||||
/// browser and there is no need for a separate Apache or IIS.
|
||||
///
|
||||
/// The following sections will explain actions within ArangoDB and show how to
|
||||
/// define them. The examples start with delivering static HTML pages - even if
|
||||
/// this is not the primary use-case for actions. The later sections will then
|
||||
/// show you how to code some pieces of your business logic and return JSON
|
||||
/// objects.
|
||||
///
|
||||
/// The interface is loosely modelled after the JavaScript classes for HTTP
|
||||
/// request and responses found in node.js and the middleware/routing aspects
|
||||
/// of connect.js and express.js.
|
||||
///
|
||||
/// Note that unlike node.js, ArangoDB is multi-threaded and there is no easy
|
||||
/// way to share state between queries inside the JavaScript engine. If such
|
||||
/// state information is required, you need to use the database itself.
|
||||
///
|
||||
/// @section UserManualActionsHelloWorld A Hello World Example
|
||||
//////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// The client API or browser sends a HTTP request to the ArangoDB server and
|
||||
/// the server returns a HTTP response to the client. A HTTP request consists
|
||||
/// of a method, normally @LIT{GET} or @LIT{POST} when using a browser, and a
|
||||
/// request path like @LIT{/hello/world}. For a real Web server there are a zillion
|
||||
/// of other thing to consider, we will ignore this for the moment. The HTTP
|
||||
/// response contains a content type, describing how to interpret the returned
|
||||
/// data, and the data itself.
|
||||
///
|
||||
/// In the following example, we want to define an action in ArangoDB, so that the
|
||||
/// server returns the HTML document
|
||||
///
|
||||
/// @code
|
||||
/// <html>
|
||||
/// <body>
|
||||
/// Hello World
|
||||
/// </body>
|
||||
/// </html>
|
||||
/// @endcode
|
||||
///
|
||||
/// if asked @LIT{GET /hello/world}.
|
||||
///
|
||||
/// The server needs to know what function to call or what document to deliver
|
||||
/// if it receives a request. This is called routing. All the routing information
|
||||
/// of ArangoDB is stored in a collection @LIT{_routing}. Each entry in this
|
||||
/// collections describes how to deal with a particular request path.
|
||||
///
|
||||
/// For the above example, add the following document to the @{_routing}
|
||||
/// collection:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: { match: "/hello/world" },
|
||||
/// ........> content: {
|
||||
/// ........> contentType: "text/html",
|
||||
/// ........> body: "<html><body>Hello World</body></html>" }});
|
||||
/// @endcode
|
||||
///
|
||||
/// In order to activate the new routing, you must either restart the server
|
||||
/// or call the internal reload function.
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("internal").reloadRouting()
|
||||
/// @endcode
|
||||
///
|
||||
/// Now use the browser and access
|
||||
///
|
||||
/// @LIT{http://localhost:8529/hello/world}
|
||||
///
|
||||
/// You should see the @LIT{Hello World} in our browser.
|
||||
///
|
||||
/// @section UserManualActionsMatches Matching an URL
|
||||
/////////////////////////////////////////////////////
|
||||
///
|
||||
/// There are a lot of options for the @LIT{url} attribute. If you define
|
||||
/// different routing for the same path, then the following simple rule is
|
||||
/// applied in order to determine which match wins: If there are two matches,
|
||||
/// then the more specific wins. I. e, if there is a wildcard match and an exact
|
||||
/// match, the exact match is prefered. If there is a short and a long match,
|
||||
/// the longer match wins.
|
||||
///
|
||||
/// @subsection UserManualActionsMatchesExact Exact Match
|
||||
///
|
||||
/// If the definition is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/world" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the match must be exact. Only the request for @LIT{/hello/world} will
|
||||
/// match, everything else, e. g. @LIT{/hello/world/my} or @LIT{/hello/world2},
|
||||
/// will not match.
|
||||
///
|
||||
/// The following definition is a short-cut for an exact match.
|
||||
///
|
||||
/// @code
|
||||
/// { url: "/hello/world" }
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection UserManualActionsMatchesPrefix Prefix Match
|
||||
///
|
||||
/// If the definition is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/world/*" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the match can be a prefix match. The requests for @LIT{/hello/world},
|
||||
/// @LIT{/hello/world/my}, and @LIT{/hello/world/how/are/you} will all
|
||||
/// match. However @LIT{/hello/world2} does not match. Prefix matches within an
|
||||
/// URL part, i. e. @LIT{/hello/world*}, are not allowed. The wildcard must
|
||||
/// occur at the end, i. e.
|
||||
///
|
||||
/// @code
|
||||
/// /hello/*/world
|
||||
/// @endcode
|
||||
///
|
||||
/// is also disallowed.
|
||||
///
|
||||
/// If you define two routes
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/world/*" } }
|
||||
/// { url: { match: "/hello/world/emil" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the second route will be used for @LIT{/hello/world/emil} because it is
|
||||
/// more specific.
|
||||
///
|
||||
/// @subsection UserManualActionsMatchesParameterized Parameterized Match
|
||||
///
|
||||
/// A parameterized match is similar to a prefix match, but the parameters are
|
||||
/// also allowed inside the URL path.
|
||||
///
|
||||
/// If the definition is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/:name/world" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the URL must have three parts, the first part being @LIT{hello} and the
|
||||
/// third part @LIT{world}. For example, @LIT{/hello/emil/world} will match,
|
||||
/// while @LIT{/hello/emil/meyer/world} will not.
|
||||
///
|
||||
/// @subsection UserManualActionsMatchesConstraint Constraint Match
|
||||
///
|
||||
/// A constraint match is similar to a parameterized match, but the parameters
|
||||
/// can carry constraints.
|
||||
///
|
||||
/// If the definition is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/:name/world", constraint: { name: "/[a-z]+/" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the URL must have three parts, the first part being @LIT{hello} and the
|
||||
/// third part @LIT{world}. The second part must be all lowercase.
|
||||
///
|
||||
/// It is possible to use more then one constraint for the same URL part.
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/:name|:id/world",
|
||||
/// constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection UserManualActionsMatchesOptional Optional Match
|
||||
///
|
||||
/// An optional match is similar to a parameterized match, but the last
|
||||
/// parameter is optional.
|
||||
///
|
||||
/// If the definition is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/:name?", constraint: { name: "/[a-z]+/" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the URL @LIT{/hello} and @LIT{/hello/emil} will match.
|
||||
///
|
||||
/// If the definitions are
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/world" } }
|
||||
/// { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } }
|
||||
/// { url: { match: "/hello/*" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// then the URL @LIT{/hello/world} will be matched by the first route, because it
|
||||
/// is the most specific. The URL @LIT{/hello/you} will be matched by the second
|
||||
/// route, because it is more specific than the prefix match.
|
||||
///
|
||||
/// @subsection UserManualActionsMatchesMethod Method Restriction
|
||||
///
|
||||
/// You can restrict the match to specific methods.
|
||||
///
|
||||
/// If the definition is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/world", methods: [ "post", "put" ] }
|
||||
/// @endcode
|
||||
///
|
||||
/// then only @LIT{POST} and @LIT{PUT} requests will match.
|
||||
///
|
||||
/// @subsection UserManualActionsMatching More on Matching
|
||||
///
|
||||
/// Remember that the more specific match wins.
|
||||
///
|
||||
/// - A match without parameter or wildcard is more specific than a match with
|
||||
/// parameters or wildcard.
|
||||
/// - A match with parameter is more specific than a match with a wildcard.
|
||||
/// - If there is more than one parameter, specificity is applied from left to
|
||||
/// right.
|
||||
///
|
||||
/// Consider the following definitions
|
||||
///
|
||||
/// @code
|
||||
/// (1) { url: { match: "/hello/world" } }
|
||||
/// (2) { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } }
|
||||
/// (3) { url: { match: "/:something/world" }
|
||||
/// (4) { url: { match: "/hello/*" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// Then
|
||||
///
|
||||
/// - @LIT{/hello/world} is match by (1)
|
||||
/// - @LIT{/hello/emil} is match by (2)
|
||||
/// - @LIT{/your/world} is match by (3)
|
||||
/// - @LIT{/hello/you} is match by (4)
|
||||
///
|
||||
/// You can write the following document into the @LIT{_routing} collection
|
||||
/// to test the above examples.
|
||||
///
|
||||
/// @code
|
||||
/// {
|
||||
/// routes: [
|
||||
/// { url: { match: "/hello/world" }, content: "route 1" },
|
||||
/// { url: { match: "/hello/:name|:id", constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }, content: "route 2" },
|
||||
/// { url: { match: "/:something/world" }, content: "route 3" },
|
||||
/// { url: { match: "/hello/*" }, content: "route 4" },
|
||||
/// ]
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// @section UserManualActionsHelloJson A Hello World Example for JSON
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// If you change the example slightly, then a JSON object will be delivered.
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/hello/json",
|
||||
/// ........> content: {
|
||||
/// ........> contentType: "application/json",
|
||||
/// ........> body: "{ \"hello\" : \"world\" }" }});
|
||||
/// arangosh> require("internal").reloadRouting()
|
||||
/// @endcode
|
||||
///
|
||||
/// Again check with your browser
|
||||
///
|
||||
/// @LIT{http://localhost:8529/hello/json}
|
||||
///
|
||||
/// Depending on your browser and installed add-ons you will either see the
|
||||
/// JSON object or a download dialog. If your browser wants to open an external
|
||||
/// application to display the JSON object, you can change the @LIT{contentType}
|
||||
/// to @LIT{"text/plain"} for the example. This makes it easier to check the
|
||||
/// example using a browser. Or use @LIT{curl} to access the server.
|
||||
///
|
||||
/// @code
|
||||
/// bash> curl "http://127.0.0.1:8529/hello/json" && echo
|
||||
/// { "hello" : "world" }
|
||||
/// @endcode
|
||||
///
|
||||
/// @section UserManualActionsContent Delivering Content
|
||||
////////////////////////////////////////////////////////
|
||||
///
|
||||
/// There are a lot of different ways on how to deliver content. We have already
|
||||
/// seen the simplest one, where static content is delivered. The fun, however,
|
||||
/// starts when delivering dynamic content.
|
||||
///
|
||||
/// @subsection UserManualActionsContentStatic Static Content
|
||||
///
|
||||
/// You can specify a body and a content-type.
|
||||
///
|
||||
/// @code
|
||||
/// { content: {
|
||||
/// contentType: "text/html",
|
||||
/// body: "<html><body>Hallo World</body></html>"
|
||||
/// }
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// If the content type is @LIT{text/plain} then you can use the short-cut
|
||||
///
|
||||
/// @code
|
||||
/// { content: "Hallo World" }
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection UserManualActionsContentAction A Simple Action
|
||||
///
|
||||
/// The simplest dynamic action is:
|
||||
///
|
||||
/// @code
|
||||
/// { action: { controller: "org/arangodb/actions", do: "echoRequest" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// It is not possible to store functions directly in the routing table, but you
|
||||
/// can call functions defined in modules. In the above example the function can
|
||||
/// be accessed from JavaScript as:
|
||||
///
|
||||
/// @LIT{require("org/arangodb/actions").echoRequest}
|
||||
///
|
||||
/// The function @LIT{echoRequest} is pre-defined. It takes the request objects
|
||||
/// and echos it in the response.
|
||||
///
|
||||
/// The signature of such a function must be
|
||||
///
|
||||
/// @code
|
||||
/// function (req, res, options, next)
|
||||
/// @endcode
|
||||
///
|
||||
/// For example
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/hello/echo",
|
||||
/// ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } });
|
||||
/// @endcode
|
||||
///
|
||||
/// Reload the routing and check
|
||||
///
|
||||
/// @LIT{http://127.0.0.1:8529/hello/echo}
|
||||
///
|
||||
/// You should see something like
|
||||
///
|
||||
/// @code
|
||||
/// {
|
||||
/// "request": {
|
||||
/// "path": "/hello/echo",
|
||||
/// "headers": {
|
||||
/// "accept-encoding": "gzip, deflate",
|
||||
/// "accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3",
|
||||
/// "connection": "keep-alive",
|
||||
/// "content-length": "0",
|
||||
/// "host": "localhost:8529",
|
||||
/// "user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0"
|
||||
/// },
|
||||
/// "requestType": "GET",
|
||||
/// "parameters": { }
|
||||
/// },
|
||||
/// "options": { }
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// The request might contain @LIT{path}, @LIT{prefix}, @LIT{suffix}, and
|
||||
/// @LIT{urlParameters} attributes. @LIT{path} is the complete path as supplied
|
||||
/// by the user and always available. If a prefix was matched, then this prefix
|
||||
/// is stored in the attribute @LIT{prefix} and the remaining URL parts are
|
||||
/// stored as an array in @LIT{suffix}. If one or more parameters were matched,
|
||||
/// then the parameter values are stored in @LIT{urlParameters}.
|
||||
///
|
||||
/// For example, if the url description is
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: "/hello/:name/:action" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// and you request the path @LIT{/hello/emil/jump}, then the request object
|
||||
/// will contain the following attribute
|
||||
///
|
||||
/// @code
|
||||
/// urlParameters: { name: "emil", action: "jump" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection UserManualActionsContentController Action Controller
|
||||
///
|
||||
/// As an alternative to the simple action, you can use controllers. A
|
||||
/// controller is a module, defines the function @LIT{get}, @LIT{put},
|
||||
/// @LIT{post}, @LIT{delete}, @LIT{head}, @LIT{patch}. If a request of
|
||||
/// the corresponding type is matched, the function will be called.
|
||||
///
|
||||
/// For example
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/hello/echo",
|
||||
/// ........> action: { controller: "org/arangodb/actions/echoController" } });
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection UserManualActionsContentPrefix Prefix Action Controller
|
||||
///
|
||||
/// The controller is selected when the definition is read. There is a
|
||||
/// more flexible, but slower and maybe insecure variant, the prefix
|
||||
/// controller.
|
||||
///
|
||||
/// Assume that the url is a prefix match
|
||||
///
|
||||
/// @code
|
||||
/// { url: { match: /hello/*" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// You can use
|
||||
///
|
||||
/// @code
|
||||
/// { action: { prefixController: "org/arangodb/actions" } }
|
||||
/// @endcode
|
||||
///
|
||||
/// to define a prefix controller. If the URL @LIT{/hello/echoController} is
|
||||
/// given, then the module @LIT{org/arangodb/actions/echoController} is used.
|
||||
///
|
||||
/// If you use an prefix controller, you should make certain that no unwanted
|
||||
/// actions are available under the prefix.
|
||||
///
|
||||
/// The definition
|
||||
///
|
||||
/// @code
|
||||
/// { action: "org/arangodb/actions" }
|
||||
/// @endcode
|
||||
///
|
||||
/// is a short-cut for a prefix controller definition.
|
||||
///
|
||||
/// @section UserManualActionsReqRes Requests and Responses
|
||||
///////////////////////////////////////////////////////////
|
||||
///
|
||||
/// The controller must define handler functions which take a request object and
|
||||
/// fill the response object.
|
||||
///
|
||||
/// A very simple example is the function @LIT{echoRequest} defined in
|
||||
/// the module @LIT{org/arangodb/actions}.
|
||||
///
|
||||
/// @code
|
||||
/// function (req, res, options, next) {
|
||||
/// var result;
|
||||
///
|
||||
/// result = { request: req, options: options };
|
||||
///
|
||||
/// res.responseCode = exports.HTTP_OK;
|
||||
/// res.contentType = "application/json";
|
||||
/// res.body = JSON.stringify(result);
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// Install it as
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/echo",
|
||||
/// ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } });
|
||||
/// @endcode
|
||||
///
|
||||
/// Reload the routing and check
|
||||
///
|
||||
/// @LIT{http://127.0.0.1:8529/hello/echo}
|
||||
///
|
||||
/// You should see something like
|
||||
///
|
||||
/// @code
|
||||
/// {
|
||||
/// "request": {
|
||||
/// "prefix": "/hello/echo",
|
||||
/// "suffix": [
|
||||
/// "hello",
|
||||
/// "echo"
|
||||
/// ],
|
||||
/// "path": "/hello/echo",
|
||||
/// "headers": {
|
||||
/// "accept-encoding": "gzip, deflate",
|
||||
/// "accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3",
|
||||
/// "connection": "keep-alive",
|
||||
/// "content-length": "0",
|
||||
/// "host": "localhost:8529",
|
||||
/// "user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0"
|
||||
/// },
|
||||
/// "requestType": "GET",
|
||||
/// "parameters": { }
|
||||
/// },
|
||||
/// "options": { }
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// You may also pass options to the called function:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/echo",
|
||||
/// ........> action: {
|
||||
/// ........> controller: "org/arangodb/actions",
|
||||
/// ........> do: "echoRequest",
|
||||
/// ........> options: { "Hallo": "World" } } });
|
||||
/// @endcode
|
||||
///
|
||||
/// You should now see the options in the result.
|
||||
///
|
||||
/// @code
|
||||
/// {
|
||||
/// "request": {
|
||||
/// ...
|
||||
/// },
|
||||
/// "options": {
|
||||
/// "Hallo": "World"
|
||||
/// }
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// @section UserManualActionsModify Modifying Request and Response
|
||||
///
|
||||
/// As we've seen in the previous examples, actions get called
|
||||
/// with the request and response objects (named @LIT{req} and @LIT{res} in the
|
||||
/// examples) passed as parameters to their handler functions.
|
||||
///
|
||||
/// The @LIT{req} object contains the incoming HTTP request, which
|
||||
/// might or might not have been modified by a previous action (if actions were
|
||||
/// chained).
|
||||
///
|
||||
/// A handler can modify the request object in place if desired. This might
|
||||
/// be useful when writing middleware (see below) that is used to intercept
|
||||
/// incoming requests, modify them and pass them to the actual handlers.
|
||||
///
|
||||
/// While modifying the request object might not be that relevant for
|
||||
/// non-middleware actions, modifying the response object definitely is. Modifying
|
||||
/// the response object is an action's only way to return data to the caller of
|
||||
/// the action.
|
||||
///
|
||||
/// We've already seen how to set the HTTP status code, the content type, and the
|
||||
/// result body. The @LIT{res} object has the following properties for these:
|
||||
/// - contentType: MIME type of the body as defined in the HTTP standard (e.g.
|
||||
/// @LIT{text/html}, @LIT{text/plain}, @LIT{application/json}, ...)
|
||||
/// - responsecode: the HTTP status code of the response as defined in the HTTP
|
||||
/// standard. Common values for actions that succeed are @LIT{200} or @LIT{201}.
|
||||
/// Please refer to the HTTP standard for more information.
|
||||
/// - body: the actual response data
|
||||
///
|
||||
/// To set or modify arbitrary headers of the response object, the @LIT{headers}
|
||||
/// property can be used. For example, to add a user-defined header to the response,
|
||||
/// the following code will do:
|
||||
///
|
||||
/// @code
|
||||
/// res.headers = res.headers || { }; // headers might or might not be present
|
||||
/// res.headers['X-Test'] = 'someValue'; // set header X-Test to "someValue"
|
||||
/// @endcode
|
||||
///
|
||||
/// This will set the additional HTTP header @LIT{X-Test} to value @LIT{someValue}.
|
||||
/// Other headers can be set as well. Note that ArangoDB might change the case
|
||||
/// of the header names to lower case when assembling the overall response that
|
||||
/// is sent to the caller.
|
||||
///
|
||||
/// It is not necessary to explicitly set a @LIT{Content-Length} header for the
|
||||
/// response as ArangoDB will calculate the content length automatically and add
|
||||
/// this header itself. ArangoDB might also add a @LIT{Connection} header itself
|
||||
/// to handle HTTP keep-alive.
|
||||
///
|
||||
/// ArangoDB also supports automatic transformation of the body data to another
|
||||
/// format. Currently, the only supported transformations are base64-encoding and
|
||||
/// base64-decoding. Using the transformations, an action can create a base64
|
||||
/// encoded body and still let ArangoDB send the non-encoded version, for example:
|
||||
///
|
||||
/// @code
|
||||
/// res.body = 'VGhpcyBpcyBhIHRlc3Q=';
|
||||
/// res.transformations = res.transformations || [ ]; // initialise
|
||||
/// res.transformations.push('base64decode'); // will base64 decode the response body
|
||||
/// @endcode
|
||||
///
|
||||
/// When ArangoDB processes the response, it will base64-decode what's in @LIT{res.body}
|
||||
/// and set the HTTP header @LIT{Content-Encoding: binary}. The opposite can be
|
||||
/// achieved with the @LIT{base64encode} transformation: ArangoDB will then automatically
|
||||
/// base64-encode the body and set a @LIT{Content-Encoding: base64} HTTP header.
|
||||
///
|
||||
/// @section UserManualActionsHandlers Writing dynamic action handlers
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// To write your own dynamic action handlers, you must put them into modules.
|
||||
///
|
||||
/// Modules are a means of organising action handlers and making them loadable
|
||||
/// under specific names.
|
||||
///
|
||||
/// To start, we'll define a simple action handler in a module @LIT{/own/test}:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._modules.save({
|
||||
/// ........> path: "/own/test",
|
||||
/// ........> content: "exports.do = function(req, res, options, next) { res.body = 'test'; res.responseCode = 200; res.contentType = 'text/html'; };",
|
||||
/// ........> autoload: true });
|
||||
/// @endcode
|
||||
///
|
||||
/// This does nothing but register a do action handler in a module @LIT{/own/test}.
|
||||
/// The action handler is not yet callable, but must be mapped to a route first.
|
||||
/// To map the action to the route @LIT{/ourtest}, execute the following command:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/ourtest",
|
||||
/// ........> action: { controller: "/own/test" } });
|
||||
/// @endcode
|
||||
///
|
||||
/// In order to see the module in action, you must either restart the server
|
||||
/// or call the internal reload function.
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("internal").reloadRouting()
|
||||
/// @endcode
|
||||
///
|
||||
/// Now use the browser and access
|
||||
///
|
||||
/// @LIT{http://localhost:8529/ourtest}
|
||||
///
|
||||
/// You will see that the module's do function has been executed.
|
||||
///
|
||||
/// @section UserManualActionsAdvanced Advanced Usages
|
||||
//////////////////////////////////////////////////////
|
||||
///
|
||||
/// For detailed information see the reference manual.
|
||||
///
|
||||
/// @subsection UserManualActionsAdvancedRedirects Redirects
|
||||
///
|
||||
/// Use the following for a permanent redirect:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> url: "/",
|
||||
/// ........> action: {
|
||||
/// ........> controller: "org/arangodb/actions",
|
||||
/// ........> do: "redirectRequest",
|
||||
/// ........> options: {
|
||||
/// ........> permanently: true,
|
||||
/// ........> destination: "http://somewhere.else/" } } });
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection UserManualActionsAdvancedBundles Routing Bundles
|
||||
///
|
||||
/// Instead of adding all routes for package separately, you can
|
||||
/// specify a bundle.
|
||||
///
|
||||
/// @code
|
||||
/// {
|
||||
/// routes: [
|
||||
/// { url: "/url1", content: "..." },
|
||||
/// { url: "/url2", content: "..." },
|
||||
/// { url: "/url3", content: "..." },
|
||||
/// ...
|
||||
/// ]
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// The advantage is, that you can put all your routes into one document
|
||||
/// and use a common prefix.
|
||||
///
|
||||
/// @code
|
||||
/// {
|
||||
/// urlPrefix: "/test",
|
||||
///
|
||||
/// routes: [
|
||||
/// { url: "/url1", content: "..." },
|
||||
/// { url: "/url2", content: "..." },
|
||||
/// { url: "/url3", content: "..." },
|
||||
/// ...
|
||||
/// ]
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// will define the URL @LIT{/test/url1}, @LIT{/test/url2}, and
|
||||
/// @LIT{/test/url3}.
|
||||
///
|
||||
/// @subsection UserManualActionsAdvancedMiddleware Writing Middleware
|
||||
///
|
||||
/// Assume, you want to log every request. In this case you can easily define
|
||||
/// an action for the whole url-space @LIT{/}. This action simply logs
|
||||
/// the requests, calls the next in line, and logs the response.
|
||||
///
|
||||
/// @code
|
||||
/// exports.logRequest = function (req, res, options, next) {
|
||||
/// console.log("received request: %s", JSON.stringify(req));
|
||||
/// next();
|
||||
/// console.log("produced response: %s", JSON.stringify(res));
|
||||
/// };
|
||||
/// @endcode
|
||||
///
|
||||
/// This function is available as @LIT{org/arangodb/actions/logRequest}.
|
||||
/// You need to tell ArangoDB that it is should use a prefix match and
|
||||
/// that the shortest match should win in this case:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> db._routing.save({
|
||||
/// ........> middleware: [
|
||||
/// ........> { url: { match: "/*" }, action: { controller: "org/arangodb/actions", do: "logRequest" } }
|
||||
/// ........> ]
|
||||
/// ........> });
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -454,6 +454,7 @@ PQIndexElements* PQIndex_top(PQIndex* idx, uint64_t numElements) {
|
|||
|
||||
bool PQIndex_update(PQIndex* idx, const PQIndexElement* oldElement, const PQIndexElement* newElement) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -71,10 +71,7 @@
|
|||
#include "RestHandler/RestImportHandler.h"
|
||||
#include "Scheduler/ApplicationScheduler.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "V8/V8LineEditor.h"
|
||||
#endif
|
||||
|
||||
#include "V8/v8-conv.h"
|
||||
#include "V8/v8-utils.h"
|
||||
#include "V8Server/ApplicationV8.h"
|
||||
|
@ -246,16 +243,23 @@ void ArangoServer::buildApplicationServer () {
|
|||
_applicationServer->setUserConfigFile(string(".arango") + string(1,TRI_DIR_SEPARATOR_CHAR) + string("arangod.conf") );
|
||||
|
||||
// .............................................................................
|
||||
// multi-threading scheduler and dispatcher
|
||||
// multi-threading scheduler
|
||||
// .............................................................................
|
||||
|
||||
_applicationScheduler = new ApplicationScheduler(_applicationServer);
|
||||
_applicationScheduler->allowMultiScheduler(true);
|
||||
|
||||
_applicationServer->addFeature(_applicationScheduler);
|
||||
|
||||
|
||||
// .............................................................................
|
||||
// dispatcher
|
||||
// .............................................................................
|
||||
|
||||
_applicationDispatcher = new ApplicationDispatcher(_applicationScheduler);
|
||||
_applicationServer->addFeature(_applicationDispatcher);
|
||||
|
||||
|
||||
// .............................................................................
|
||||
// V8 engine
|
||||
// .............................................................................
|
||||
|
@ -304,11 +308,16 @@ void ArangoServer::buildApplicationServer () {
|
|||
// daemon and supervisor mode
|
||||
// .............................................................................
|
||||
|
||||
|
||||
additional[ApplicationServer::OPTIONS_CMDLINE]
|
||||
("console", "do not start as server, start a JavaScript emergency console instead")
|
||||
("upgrade", "perform a database upgrade")
|
||||
;
|
||||
|
||||
additional[ApplicationServer::OPTIONS_HIDDEN]
|
||||
("no-upgrade", "skip a database upgrade")
|
||||
;
|
||||
|
||||
#ifdef TRI_ENABLE_MRUBY
|
||||
additional[ApplicationServer::OPTIONS_CMDLINE]
|
||||
("ruby-console", "do not start as server, start a Ruby emergency console instead")
|
||||
|
@ -380,6 +389,7 @@ void ArangoServer::buildApplicationServer () {
|
|||
// endpoint server
|
||||
// .............................................................................
|
||||
|
||||
|
||||
_applicationEndpointServer = new ApplicationEndpointServer(_applicationServer,
|
||||
_applicationScheduler,
|
||||
_applicationDispatcher,
|
||||
|
@ -396,6 +406,7 @@ void ArangoServer::buildApplicationServer () {
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TRI_HAVE_ICU
|
||||
// .............................................................................
|
||||
// set language of default collator
|
||||
|
@ -415,6 +426,7 @@ void ArangoServer::buildApplicationServer () {
|
|||
LOGGER_INFO << "using default language '" << Utf8Helper::DefaultUtf8Helper.getCollatorLanguage() << "'" ;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// .............................................................................
|
||||
// disable access to the HTML admin interface
|
||||
|
@ -518,6 +530,7 @@ void ArangoServer::buildApplicationServer () {
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -548,6 +561,11 @@ int ArangoServer::startupServer () {
|
|||
_applicationV8->performUpgrade();
|
||||
}
|
||||
|
||||
// skip an upgrade even if VERSION is missing
|
||||
if (_applicationServer->programOptions().has("no-upgrade")) {
|
||||
_applicationV8->skipUpgrade();
|
||||
}
|
||||
|
||||
#if TRI_ENABLE_MRUBY
|
||||
_applicationMR->setVocbase(_vocbase);
|
||||
_applicationMR->setConcurrency(_dispatcherThreads);
|
||||
|
@ -574,6 +592,7 @@ int ArangoServer::startupServer () {
|
|||
httpOptions._contexts.insert("api");
|
||||
httpOptions._contexts.insert("admin");
|
||||
|
||||
|
||||
// create the server
|
||||
_applicationEndpointServer->buildServers();
|
||||
|
||||
|
@ -603,13 +622,16 @@ int ArangoServer::startupServer () {
|
|||
LOGGER_INFO << "ArangoDB (version " << TRIAGENS_VERSION << ") is ready for business";
|
||||
LOGGER_INFO << "Have Fun!";
|
||||
|
||||
|
||||
_applicationServer->wait();
|
||||
|
||||
|
||||
// .............................................................................
|
||||
// and cleanup
|
||||
// .............................................................................
|
||||
|
||||
_applicationServer->stop();
|
||||
|
||||
closeDatabase();
|
||||
|
||||
return 0;
|
||||
|
@ -646,9 +668,16 @@ int ArangoServer::executeConsole (OperationMode::server_operation_mode_e mode) {
|
|||
// set-up V8 context
|
||||
_applicationV8->setVocbase(_vocbase);
|
||||
_applicationV8->setConcurrency(1);
|
||||
|
||||
if (_applicationServer->programOptions().has("upgrade")) {
|
||||
_applicationV8->performUpgrade();
|
||||
}
|
||||
|
||||
// skip an upgrade even if VERSION is missing
|
||||
if (_applicationServer->programOptions().has("no-upgrade")) {
|
||||
_applicationV8->skipUpgrade();
|
||||
}
|
||||
|
||||
_applicationV8->disableActions();
|
||||
|
||||
ok = _applicationV8->prepare();
|
||||
|
@ -763,7 +792,7 @@ int ArangoServer::executeConsole (OperationMode::server_operation_mode_e mode) {
|
|||
v8::TryCatch tryCatch;
|
||||
|
||||
for (size_t i = 0; i < _scriptFile.size(); ++i) {
|
||||
bool r = TRI_LoadJavaScriptFile(context->_context, _scriptFile[i].c_str());
|
||||
bool r = TRI_ExecuteGlobalJavaScriptFile(_scriptFile[i].c_str());
|
||||
|
||||
if (! r) {
|
||||
LOGGER_FATAL << "cannot load script '" << _scriptFile[i] << ", giving up";
|
||||
|
@ -817,7 +846,6 @@ int ArangoServer::executeConsole (OperationMode::server_operation_mode_e mode) {
|
|||
// .............................................................................
|
||||
|
||||
case OperationMode::MODE_CONSOLE: {
|
||||
#ifndef _WIN32
|
||||
V8LineEditor console(context->_context, ".arango");
|
||||
|
||||
console.open(true);
|
||||
|
@ -851,7 +879,6 @@ int ArangoServer::executeConsole (OperationMode::server_operation_mode_e mode) {
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -874,7 +901,11 @@ int ArangoServer::executeConsole (OperationMode::server_operation_mode_e mode) {
|
|||
closeDatabase();
|
||||
Random::shutdown();
|
||||
|
||||
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (!ok) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1056,6 +1087,8 @@ int ArangoServer::executeRubyConsole () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ArangoServer::openDatabase () {
|
||||
TRI_InitialiseVocBase();
|
||||
|
||||
_vocbase = TRI_OpenVocBase(_databasePath.c_str());
|
||||
|
||||
if (! _vocbase) {
|
||||
|
@ -1082,6 +1115,7 @@ void ArangoServer::closeDatabase () {
|
|||
TRI_DestroyVocBase(_vocbase);
|
||||
TRI_Free(TRI_UNKNOWN_MEM_ZONE, _vocbase);
|
||||
_vocbase = 0;
|
||||
TRI_ShutdownVocBase();
|
||||
|
||||
LOGGER_INFO << "ArangoDB has been shut down";
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
#ifndef TRIAGENS_REST_SERVER_ARANGO_SERVER_H
|
||||
#define TRIAGENS_REST_SERVER_ARANGO_SERVER_H 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "BasicsC/win-utils.h"
|
||||
#endif
|
||||
|
||||
#include "Rest/AnyServer.h"
|
||||
#include "Rest/OperationMode.h"
|
||||
|
||||
|
|
|
@ -50,20 +50,52 @@ using namespace triagens::arango;
|
|||
int main (int argc, char* argv[]) {
|
||||
int res;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// ...........................................................................
|
||||
// Call this function to do various initialistions for windows only
|
||||
// ...........................................................................
|
||||
|
||||
// ...........................................................................
|
||||
// Uncomment this to call this for extended debug information.
|
||||
// If you familiar with valgrind ... then this is not like that, however
|
||||
// you do get some similar functionality.
|
||||
// ...........................................................................
|
||||
//res = initialiseWindows(TRI_WIN_INITIAL_SET_DEBUG_FLAG, 0);
|
||||
|
||||
res = initialiseWindows(TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER, 0);
|
||||
if (res != 0) {
|
||||
_exit(1);
|
||||
}
|
||||
res = initialiseWindows(TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL, 0);
|
||||
if (res != 0) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TRIAGENS_RESULT_GENERATOR_INITIALISE(argc, argv);
|
||||
TRI_InitialiseVocBase();
|
||||
|
||||
// create and start a ArangoDB server
|
||||
ArangoServer server(argc, argv);
|
||||
|
||||
|
||||
res = server.start();
|
||||
|
||||
|
||||
// shutdown
|
||||
TRI_ShutdownVocBase();
|
||||
TRIAGENS_RESULT_GENERATOR_SHUTDOWN;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// ...........................................................................
|
||||
// TODO: need a terminate function for windows to be called and cleanup
|
||||
// any windows specific stuff.
|
||||
// TODO: find the memory deallocation/allocation error
|
||||
// ...........................................................................
|
||||
|
||||
res = finaliseWindows(TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL, 0);
|
||||
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -501,6 +501,7 @@ void* TRI_EndNodeSkipList(TRI_skiplist_t* skiplist) {
|
|||
int TRI_InsertElementSkipList(TRI_skiplist_t* skiplist, void* element, bool overwrite) {
|
||||
// Use TRI_InsertKeySkipList instead of calling this method
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -887,6 +888,7 @@ void* TRI_LeftLookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) {
|
|||
|
||||
void* TRI_LookupByElementSkipList (TRI_skiplist_t* skiplist, void* element) {
|
||||
assert(false); // there is no way we can be here
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1207,6 +1209,7 @@ int TRI_RemoveElementSkipList (TRI_skiplist_t* skiplist, void* element, void* ol
|
|||
int TRI_RemoveKeySkipList (TRI_skiplist_t* skiplist, void* key, void* old) {
|
||||
// Use the TRI_RemoveElementSkipList method instead.
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1695,6 +1698,7 @@ void* TRI_LeftLookupByKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key
|
|||
|
||||
void* TRI_LookupByElementSkipListMulti(TRI_skiplist_multi_t* skiplist, void* element) {
|
||||
assert(false); // there is no way you should be here
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1705,6 +1709,7 @@ void* TRI_LookupByElementSkipListMulti(TRI_skiplist_multi_t* skiplist, void* ele
|
|||
|
||||
void* TRI_LookupByKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key) {
|
||||
assert(false); // there is no way you should be here
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1954,6 +1959,7 @@ int TRI_InsertElementSkipListMulti(TRI_skiplist_multi_t* skiplist, void* element
|
|||
int TRI_InsertKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key, void* element, bool overwrite) {
|
||||
// Use TRI_InsertelementSkipList instead of calling this method
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2183,6 +2189,7 @@ int TRI_RemoveElementSkipListMulti (TRI_skiplist_multi_t* skiplist, void* elemen
|
|||
int TRI_RemoveKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key, void* old) {
|
||||
// Use the TRI_RemoveElementSkipListMulti method instead.
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -987,6 +987,7 @@ bool SkiplistIndex_update(SkiplistIndex* skiplistIndex, const SkiplistIndexEleme
|
|||
// then adds the afterElement -- should never be called here
|
||||
// call SkiplistIndex_remove first and then SkiplistIndex_add
|
||||
assert(false);
|
||||
return false; // shuts the VC++ up
|
||||
}
|
||||
|
||||
|
||||
|
@ -1390,6 +1391,7 @@ int MultiSkiplistIndex_remove(SkiplistIndex* skiplistIndex, SkiplistIndexElement
|
|||
|
||||
bool MultiSkiplistIndex_update(SkiplistIndex* skiplistIndex, SkiplistIndexElement* beforeElement, SkiplistIndexElement* afterElement) {
|
||||
assert(false); // should never be called directly
|
||||
return false; // shuts the VC++ up
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -180,6 +180,7 @@ ApplicationV8::ApplicationV8 (string const& binaryPath)
|
|||
_actionPath(),
|
||||
_useActions(true),
|
||||
_performUpgrade(false),
|
||||
_skipUpgrade(false),
|
||||
_gcInterval(1000),
|
||||
_gcFrequency(10.0),
|
||||
_v8Options(""),
|
||||
|
@ -239,6 +240,14 @@ void ApplicationV8::performUpgrade () {
|
|||
_performUpgrade = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief skip a database upgrade
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ApplicationV8::skipUpgrade () {
|
||||
_skipUpgrade = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enters an context
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -511,6 +520,7 @@ void ApplicationV8::setupOptions (map<string, basics::ProgramOptionsDescription>
|
|||
bool ApplicationV8::prepare () {
|
||||
LOGGER_DEBUG << "V8 version: " << v8::V8::GetVersion();
|
||||
|
||||
|
||||
// use a minimum of 1 second for GC
|
||||
if (_gcFrequency < 1) {
|
||||
_gcFrequency = 1;
|
||||
|
@ -529,7 +539,6 @@ bool ApplicationV8::prepare () {
|
|||
// set up the startup loader
|
||||
if (_startupPath.empty()) {
|
||||
LOGGER_INFO << "using built-in JavaScript startup files";
|
||||
|
||||
_startupLoader.defineScript("common/bootstrap/modules.js", JS_common_bootstrap_modules);
|
||||
_startupLoader.defineScript("common/bootstrap/monkeypatches.js", JS_common_bootstrap_monkeypatches);
|
||||
_startupLoader.defineScript("common/bootstrap/print.js", JS_common_bootstrap_print);
|
||||
|
@ -557,6 +566,7 @@ bool ApplicationV8::prepare () {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (_v8Options.size() > 0) {
|
||||
LOGGER_INFO << "using V8 options '" << _v8Options << "'";
|
||||
v8::V8::SetFlagsFromString(_v8Options.c_str(), _v8Options.size());
|
||||
|
@ -669,6 +679,7 @@ bool ApplicationV8::prepareV8Instance (const size_t i) {
|
|||
TRI_InitV8VocBridge(context->_context, _vocbase, i);
|
||||
TRI_InitV8Queries(context->_context);
|
||||
|
||||
|
||||
if (_useActions) {
|
||||
TRI_InitV8Actions(context->_context, this);
|
||||
}
|
||||
|
@ -693,7 +704,7 @@ bool ApplicationV8::prepareV8Instance (const size_t i) {
|
|||
}
|
||||
|
||||
|
||||
if (i == 0) {
|
||||
if (i == 0 && ! _skipUpgrade) {
|
||||
LOGGER_DEBUG << "running database version check";
|
||||
|
||||
const string script = _startupLoader.buildScript(JS_server_version_check);
|
||||
|
|
|
@ -206,6 +206,12 @@ namespace triagens {
|
|||
|
||||
void performUpgrade ();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief disable the database version check
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void skipUpgrade ();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enters an context
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -401,6 +407,12 @@ namespace triagens {
|
|||
|
||||
bool _performUpgrade;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief perform a database upgrade
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _skipUpgrade;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief JavaScript garbage collection interval (each x requests)
|
||||
///
|
||||
|
|
|
@ -45,7 +45,6 @@ using namespace triagens::basics;
|
|||
using namespace triagens::rest;
|
||||
using namespace triagens::arango;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- forward declarations
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -2839,7 +2839,7 @@ static v8::Handle<v8::Value> JS_UpgradeVocbaseCol (v8::Arguments const& argv) {
|
|||
break;
|
||||
}
|
||||
|
||||
off_t paddedSize = ((marker._size + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
off_t paddedSize = TRI_DF_ALIGN_BLOCK(marker._size);
|
||||
|
||||
char payload[paddedSize];
|
||||
char* p = (char*) &payload;
|
||||
|
@ -2882,7 +2882,7 @@ static v8::Handle<v8::Value> JS_UpgradeVocbaseCol (v8::Arguments const& argv) {
|
|||
|
||||
sprintf(didBuffer,"%d", (unsigned int) oldMarker->_did);
|
||||
keySize = strlen(didBuffer) + 1;
|
||||
keyBodySize = ((keySize + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
keyBodySize = TRI_DF_ALIGN_BLOCK(keySize);
|
||||
keyBody = (char*) TRI_Allocate(TRI_CORE_MEM_ZONE, keyBodySize, true);
|
||||
TRI_CopyString(keyBody, didBuffer, keySize);
|
||||
|
||||
|
@ -2942,7 +2942,7 @@ static v8::Handle<v8::Value> JS_UpgradeVocbaseCol (v8::Arguments const& argv) {
|
|||
toSize = strlen(toDidBuffer) + 1;
|
||||
fromSize = strlen(fromDidBuffer) + 1;
|
||||
|
||||
keyBodySize = ((keySize + toSize + fromSize + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
keyBodySize = TRI_DF_ALIGN_BLOCK(keySize + toSize + fromSize);
|
||||
keyBody = (char*) TRI_Allocate(TRI_CORE_MEM_ZONE, keyBodySize, true);
|
||||
|
||||
TRI_CopyString(keyBody, didBuffer, keySize);
|
||||
|
@ -2996,7 +2996,7 @@ static v8::Handle<v8::Value> JS_UpgradeVocbaseCol (v8::Arguments const& argv) {
|
|||
|
||||
sprintf(didBuffer,"%d", (unsigned int) oldMarker->_did);
|
||||
keySize = strlen(didBuffer) + 1;
|
||||
keyBodySize = ((keySize + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
keyBodySize = TRI_DF_ALIGN_BLOCK(keySize);
|
||||
keyBody = (char*) TRI_Allocate(TRI_CORE_MEM_ZONE, keyBodySize, true);
|
||||
TRI_CopyString(keyBody, didBuffer, keySize);
|
||||
|
||||
|
|
|
@ -271,6 +271,7 @@ void TRI_CleanupVocBase (void* data) {
|
|||
// server shutdown
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TRI_DestroyVectorPointer(&collections);
|
||||
|
|
|
@ -242,9 +242,9 @@ static bool CheckCollection (TRI_collection_t* collection) {
|
|||
TRI_PushBackVectorPointer(&all, datafile);
|
||||
|
||||
// check the document header
|
||||
ptr = datafile->_data;
|
||||
ptr += ((sizeof(TRI_df_header_marker_t) + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;;
|
||||
cm = (TRI_col_header_marker_t*) ptr;
|
||||
ptr = datafile->_data;
|
||||
ptr += TRI_DF_ALIGN_BLOCK(sizeof(TRI_df_header_marker_t));
|
||||
cm = (TRI_col_header_marker_t*) ptr;
|
||||
|
||||
if (cm->base._type != TRI_COL_MARKER_HEADER) {
|
||||
LOG_ERROR("collection header mismatch in file '%s', expected TRI_COL_MARKER_HEADER, found %lu",
|
||||
|
@ -346,9 +346,7 @@ static bool CheckCollection (TRI_collection_t* collection) {
|
|||
else {
|
||||
collection->_lastError = datafile->_lastError;
|
||||
stop = true;
|
||||
|
||||
LOG_ERROR("cannot rename sealed log-file to %s, this should not happen: %s", filename, TRI_last_error());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -677,7 +675,9 @@ TRI_collection_t* TRI_CreateCollection (TRI_vocbase_t* vocbase,
|
|||
}
|
||||
|
||||
InitCollection(vocbase, collection, filename, parameter);
|
||||
|
||||
/* PANAIA: 1) the parameter file if it exists must be removed
|
||||
2) if collection
|
||||
*/
|
||||
// return collection
|
||||
return collection;
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ TRI_col_state_e;
|
|||
typedef uint32_t TRI_col_version_t;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief collection type
|
||||
/// @brief collection enum
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef enum {
|
||||
|
@ -198,10 +198,15 @@ TRI_col_type_e;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct TRI_col_header_marker_s {
|
||||
TRI_df_marker_t base;
|
||||
TRI_df_marker_t base; // 24 bytes
|
||||
|
||||
TRI_col_type_e _type;
|
||||
TRI_voc_cid_t _cid;
|
||||
TRI_col_type_t _type; // 4 bytes
|
||||
|
||||
#ifdef TRI_PADDING_32
|
||||
char _padding_col_header_marker[4];
|
||||
#endif
|
||||
|
||||
TRI_voc_cid_t _cid; // 8 bytes
|
||||
}
|
||||
TRI_col_header_marker_t;
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
/// @author Copyright 2011, triagens GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <BasicsC/win-utils.h>
|
||||
#endif
|
||||
|
||||
#include "datafile.h"
|
||||
|
||||
#include <BasicsC/hashes.h>
|
||||
|
@ -33,6 +37,7 @@
|
|||
#include <BasicsC/strings.h>
|
||||
#include <BasicsC/files.h>
|
||||
|
||||
|
||||
// #define DEBUG_DATAFILE 1
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -121,11 +126,11 @@ static int TruncateDatafile (TRI_datafile_t* datafile, TRI_voc_size_t vocSize) {
|
|||
}
|
||||
|
||||
// create sparse file
|
||||
offset = lseek(fd, maximalSize - 1, SEEK_SET);
|
||||
offset = TRI_LSEEK(fd, maximalSize - 1, SEEK_SET);
|
||||
|
||||
if (offset == (off_t) -1) {
|
||||
TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
// remove empty file
|
||||
TRI_UnlinkFile(filename);
|
||||
|
@ -135,11 +140,11 @@ static int TruncateDatafile (TRI_datafile_t* datafile, TRI_voc_size_t vocSize) {
|
|||
}
|
||||
|
||||
zero = 0;
|
||||
res = write(fd, &zero, 1);
|
||||
res = TRI_WRITE(fd, &zero, 1);
|
||||
|
||||
if (res < 0) {
|
||||
TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
// remove empty file
|
||||
TRI_UnlinkFile(filename);
|
||||
|
@ -149,11 +154,11 @@ static int TruncateDatafile (TRI_datafile_t* datafile, TRI_voc_size_t vocSize) {
|
|||
}
|
||||
|
||||
// memory map the data
|
||||
res = TRI_MMFile(0, maximalSize, PROT_WRITE | PROT_READ, MAP_SHARED, &fd, &mmHandle, 0, &data);
|
||||
res = TRI_MMFile(0, maximalSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, &mmHandle, 0, &data);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_set_errno(res);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
// remove empty file
|
||||
TRI_UnlinkFile(filename);
|
||||
|
@ -166,15 +171,21 @@ static int TruncateDatafile (TRI_datafile_t* datafile, TRI_voc_size_t vocSize) {
|
|||
memcpy(data, datafile->_data, vocSize);
|
||||
|
||||
// patch the datafile structure
|
||||
res = TRI_UNMMFile(datafile->_data, datafile->_maximalSize, &(datafile->_fd), &(datafile->_mmHandle));
|
||||
res = TRI_UNMMFile(datafile->_data, datafile->_maximalSize, datafile->_fd, &(datafile->_mmHandle));
|
||||
|
||||
if (res < 0) {
|
||||
LOG_ERROR("munmap failed with: %d", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
close(datafile->_fd);
|
||||
// the datafile->_mmHandle object has been closed in the underlying TRI_UNMMFile(...) call above
|
||||
// .............................................................................................
|
||||
// For windows: Mem mapped files use handles
|
||||
// the datafile->_mmHandle handle object has been closed in the underlying
|
||||
// TRI_UNMMFile(...) call above so we do not need to close it for the associated file below
|
||||
// .............................................................................................
|
||||
|
||||
TRI_CLOSE(datafile->_fd);
|
||||
|
||||
|
||||
datafile->_data = data;
|
||||
datafile->_next = (char*)(data) + vocSize;
|
||||
|
@ -287,7 +298,7 @@ static TRI_df_scan_t ScanDatafile (TRI_datafile_t const* datafile) {
|
|||
|
||||
TRI_PushBackVector(&scan._entries, &entry);
|
||||
|
||||
size = ((marker->_size + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
size = TRI_DF_ALIGN_BLOCK(marker->_size);
|
||||
currentSize += size;
|
||||
|
||||
if (marker->_type == TRI_DF_MARKER_FOOTER) {
|
||||
|
@ -385,7 +396,7 @@ static bool CheckDatafile (TRI_datafile_t* datafile) {
|
|||
|
||||
TRI_UpdateTickVocBase(marker->_tick);
|
||||
|
||||
size = ((marker->_size + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
size = TRI_DF_ALIGN_BLOCK(marker->_size);
|
||||
currentSize += size;
|
||||
|
||||
if (marker->_type == TRI_DF_MARKER_FOOTER) {
|
||||
|
@ -422,8 +433,11 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
struct stat status;
|
||||
TRI_df_header_marker_t header;
|
||||
void* mmHandle;
|
||||
|
||||
// open the file
|
||||
|
||||
// ..........................................................................
|
||||
// attempt to open a datafile file
|
||||
// ..........................................................................
|
||||
|
||||
fd = TRI_OPEN(filename, O_RDWR);
|
||||
|
||||
if (fd < 0) {
|
||||
|
@ -434,12 +448,14 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compute the size of the file
|
||||
res = fstat(fd, &status);
|
||||
|
||||
if (res < 0) {
|
||||
TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
LOG_ERROR("cannot get status of datafile '%s': %s", filename, TRI_last_error());
|
||||
|
||||
|
@ -451,7 +467,7 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
|
||||
if (size < sizeof(TRI_df_header_marker_t) + sizeof(TRI_df_footer_marker_t)) {
|
||||
TRI_set_errno(TRI_ERROR_ARANGO_CORRUPTED_DATAFILE);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
LOG_ERROR("datafile '%s' is corrupted, size is only %u", filename, (unsigned int) size);
|
||||
|
||||
|
@ -467,7 +483,7 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
if (! ok) {
|
||||
LOG_ERROR("cannot read datafile header from '%s': %s", filename, TRI_last_error());
|
||||
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -480,7 +496,7 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
LOG_ERROR("corrupted datafile header read from '%s'", filename);
|
||||
|
||||
if (! ignoreErrors) {
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -495,7 +511,7 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
filename);
|
||||
|
||||
if (! ignoreErrors) {
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -509,11 +525,11 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
|||
}
|
||||
|
||||
// map datafile into memory
|
||||
res = TRI_MMFile(0, size, PROT_READ, MAP_SHARED, &fd, &mmHandle, 0, &data);
|
||||
res = TRI_MMFile(0, size, PROT_READ, MAP_SHARED, fd, &mmHandle, 0, &data);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_set_errno(res);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
LOG_ERROR("cannot memory map file '%s': '%d'", filename, res);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -589,12 +605,12 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
}
|
||||
|
||||
// create sparse file
|
||||
offset = lseek(fd, maximalSize - 1, SEEK_SET);
|
||||
offset = TRI_LSEEK(fd, maximalSize - 1, SEEK_SET);
|
||||
|
||||
if (offset == (off_t) -1) {
|
||||
TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
close(fd);
|
||||
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
// remove empty file
|
||||
TRI_UnlinkFile(filename);
|
||||
|
||||
|
@ -603,11 +619,11 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
}
|
||||
|
||||
zero = 0;
|
||||
res = write(fd, &zero, 1);
|
||||
res = TRI_WRITE(fd, &zero, 1);
|
||||
|
||||
if (res < 0) {
|
||||
TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
// remove empty file
|
||||
TRI_UnlinkFile(filename);
|
||||
|
@ -617,11 +633,11 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
}
|
||||
|
||||
// memory map the data
|
||||
res = TRI_MMFile(0, maximalSize, PROT_WRITE | PROT_READ, MAP_SHARED, &fd, &mmHandle, 0, &data);
|
||||
res = TRI_MMFile(0, maximalSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, &mmHandle, 0, &data);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_set_errno(res);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
// remove empty file
|
||||
TRI_UnlinkFile(filename);
|
||||
|
@ -630,20 +646,22 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// get next tick
|
||||
tick = TRI_NewTickVocBase();
|
||||
|
||||
// create datafile structure
|
||||
datafile = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_datafile_t), false);
|
||||
datafile = (TRI_datafile_t*)(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_datafile_t), false));
|
||||
|
||||
if (datafile == NULL) {
|
||||
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
||||
close(fd);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
LOG_ERROR("out-of-memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
InitDatafile(datafile,
|
||||
TRI_DuplicateString(filename),
|
||||
fd,
|
||||
|
@ -653,6 +671,7 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
tick,
|
||||
data);
|
||||
|
||||
|
||||
datafile->_state = TRI_DF_STATE_WRITE;
|
||||
|
||||
// create the header
|
||||
|
@ -666,6 +685,7 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
header._maximalSize = maximalSize;
|
||||
header._fid = tick;
|
||||
|
||||
|
||||
// create CRC
|
||||
TRI_FillCrcMarkerDatafile(&header.base, sizeof(TRI_df_header_marker_t), 0, 0, 0, 0);
|
||||
|
||||
|
@ -677,10 +697,9 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
}
|
||||
|
||||
if (result != TRI_ERROR_NO_ERROR) {
|
||||
LOG_ERROR("cannot write header to datafile '%s'",
|
||||
filename);
|
||||
TRI_UNMMFile(datafile->_data, datafile->_maximalSize, &fd, &mmHandle);
|
||||
close(fd);
|
||||
LOG_ERROR("cannot write header to datafile '%s'",filename);
|
||||
TRI_UNMMFile(datafile->_data, datafile->_maximalSize, fd, &mmHandle);
|
||||
TRI_CLOSE(fd);
|
||||
|
||||
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, datafile->_filename);
|
||||
TRI_Free(TRI_UNKNOWN_MEM_ZONE, datafile);
|
||||
|
@ -688,11 +707,13 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename, TRI_voc_size_t maximal
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
LOG_DEBUG("created datafile '%s' of size %u and page-size %u",
|
||||
filename,
|
||||
(unsigned int) maximalSize,
|
||||
(unsigned int) PageSize);
|
||||
|
||||
|
||||
return datafile;
|
||||
}
|
||||
|
||||
|
@ -822,7 +843,7 @@ int TRI_ReserveElementDatafile (TRI_datafile_t* datafile,
|
|||
TRI_voc_size_t size,
|
||||
TRI_df_marker_t** position) {
|
||||
*position = 0;
|
||||
size = ((size + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
size = TRI_DF_ALIGN_BLOCK(size);
|
||||
|
||||
if (datafile->_state != TRI_DF_STATE_WRITE) {
|
||||
if (datafile->_state == TRI_DF_STATE_READ) {
|
||||
|
@ -961,7 +982,7 @@ bool TRI_IterateDatafile (TRI_datafile_t* datafile,
|
|||
return false;
|
||||
}
|
||||
|
||||
size = ((marker->_size + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
size = TRI_DF_ALIGN_BLOCK(marker->_size);
|
||||
ptr += size;
|
||||
}
|
||||
|
||||
|
@ -988,8 +1009,8 @@ TRI_datafile_t* TRI_OpenDatafile (char const* filename) {
|
|||
ok = CheckDatafile(datafile);
|
||||
|
||||
if (!ok) {
|
||||
TRI_UNMMFile(datafile->_data, datafile->_maximalSize, &(datafile->_fd), &(datafile->_mmHandle));
|
||||
close(datafile->_fd);
|
||||
TRI_UNMMFile(datafile->_data, datafile->_maximalSize, datafile->_fd, &(datafile->_mmHandle));
|
||||
TRI_CLOSE(datafile->_fd);
|
||||
|
||||
LOG_ERROR("datafile '%s' is corrupt", datafile->_filename);
|
||||
// must free datafile here
|
||||
|
@ -1001,7 +1022,7 @@ TRI_datafile_t* TRI_OpenDatafile (char const* filename) {
|
|||
// change to read-write if no footer has been found
|
||||
if (! datafile->_isSealed) {
|
||||
datafile->_state = TRI_DF_STATE_WRITE;
|
||||
TRI_ProtectMMFile(datafile->_data, datafile->_maximalSize, PROT_READ | PROT_WRITE, &(datafile->_fd), &(datafile->_mmHandle));
|
||||
TRI_ProtectMMFile(datafile->_data, datafile->_maximalSize, PROT_READ | PROT_WRITE, datafile->_fd, &(datafile->_mmHandle));
|
||||
}
|
||||
|
||||
return datafile;
|
||||
|
@ -1032,7 +1053,7 @@ TRI_datafile_t* TRI_ForcedOpenDatafile (char const* filename) {
|
|||
else {
|
||||
if (! datafile->_isSealed) {
|
||||
datafile->_state = TRI_DF_STATE_WRITE;
|
||||
TRI_ProtectMMFile(datafile->_data, datafile->_maximalSize, PROT_READ | PROT_WRITE, &(datafile->_fd), &(datafile->_mmHandle));
|
||||
TRI_ProtectMMFile(datafile->_data, datafile->_maximalSize, PROT_READ | PROT_WRITE, datafile->_fd, &(datafile->_mmHandle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1046,8 +1067,7 @@ TRI_datafile_t* TRI_ForcedOpenDatafile (char const* filename) {
|
|||
bool TRI_CloseDatafile (TRI_datafile_t* datafile) {
|
||||
if (datafile->_state == TRI_DF_STATE_READ || datafile->_state == TRI_DF_STATE_WRITE) {
|
||||
int res;
|
||||
|
||||
res = TRI_UNMMFile(datafile->_data, datafile->_maximalSize, &(datafile->_fd), &(datafile->_mmHandle));
|
||||
res = TRI_UNMMFile(datafile->_data, datafile->_maximalSize, datafile->_fd, &(datafile->_mmHandle));
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
LOG_ERROR("munmap failed with: %d", res);
|
||||
|
@ -1057,7 +1077,7 @@ bool TRI_CloseDatafile (TRI_datafile_t* datafile) {
|
|||
}
|
||||
|
||||
else {
|
||||
close(datafile->_fd);
|
||||
TRI_CLOSE(datafile->_fd);
|
||||
|
||||
datafile->_state = TRI_DF_STATE_CLOSED;
|
||||
datafile->_data = 0;
|
||||
|
@ -1155,6 +1175,7 @@ int TRI_SealDatafile (TRI_datafile_t* datafile) {
|
|||
ok = TRI_msync(datafile->_fd, datafile->_mmHandle, datafile->_data, ((char*) datafile->_data) + datafile->_currentSize);
|
||||
|
||||
if (! ok) {
|
||||
abort();
|
||||
datafile->_state = TRI_DF_STATE_WRITE_ERROR;
|
||||
|
||||
if (errno == ENOSPC) {
|
||||
|
@ -1171,14 +1192,28 @@ int TRI_SealDatafile (TRI_datafile_t* datafile) {
|
|||
datafile->_synced = datafile->_written;
|
||||
datafile->_nSynced = datafile->_nWritten;
|
||||
|
||||
// change the datafile to read-only
|
||||
TRI_ProtectMMFile(datafile->_data, datafile->_maximalSize, PROT_READ, &(datafile->_fd), &(datafile->_mmHandle));
|
||||
/*
|
||||
TODO: do we have to unmap file? That is, release the memory which has been allocated for
|
||||
this file? At the moment the windows of function TRI_ProtectMMFile does nothing.
|
||||
*/
|
||||
TRI_ProtectMMFile(datafile->_data, datafile->_maximalSize, PROT_READ, datafile->_fd, &(datafile->_mmHandle));
|
||||
|
||||
// truncate datafile
|
||||
if (ok) {
|
||||
res = ftruncate(datafile->_fd, datafile->_currentSize);
|
||||
|
||||
#ifdef _WIN32
|
||||
res = 0;
|
||||
/*
|
||||
res = ftruncate(datafile->_fd, datafile->_currentSize);
|
||||
Linux centric problems:
|
||||
Under windows can not reduce size of the memory mapped file without unmappping it!
|
||||
However, apparently we may have users
|
||||
*/
|
||||
#else
|
||||
res = ftruncate(datafile->_fd, datafile->_currentSize);
|
||||
#endif
|
||||
|
||||
if (res < 0) {
|
||||
abort();
|
||||
LOG_ERROR("cannot truncate datafile '%s': %s", datafile->_filename, TRI_last_error());
|
||||
datafile->_lastError = TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
ok = false;
|
||||
|
@ -1188,7 +1223,11 @@ int TRI_SealDatafile (TRI_datafile_t* datafile) {
|
|||
datafile->_state = TRI_DF_STATE_READ;
|
||||
}
|
||||
|
||||
return ok ? TRI_ERROR_NO_ERROR : datafile->_lastError;
|
||||
if (!ok) {
|
||||
return datafile->_lastError;
|
||||
}
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -112,7 +112,7 @@ extern "C" {
|
|||
/// @brief alignment in datafile blocks
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_DF_BLOCK_ALIGN (8)
|
||||
#define TRI_DF_BLOCK_ALIGNMENT (8)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
|
@ -302,6 +302,9 @@ typedef struct TRI_df_marker_s {
|
|||
|
||||
TRI_voc_tick_t _tick; // 8 bytes, will be generated
|
||||
|
||||
#ifdef TRI_PADDING_32
|
||||
char _padding_df_marker[4];
|
||||
#endif
|
||||
}
|
||||
TRI_df_marker_t;
|
||||
|
||||
|
@ -336,11 +339,11 @@ TRI_df_marker_t;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct TRI_df_header_marker_s {
|
||||
TRI_df_marker_t base;
|
||||
TRI_df_marker_t base; // 24 bytes
|
||||
|
||||
TRI_df_version_t _version;
|
||||
TRI_voc_size_t _maximalSize;
|
||||
TRI_voc_tick_t _fid;
|
||||
TRI_df_version_t _version; // 4 bytes
|
||||
TRI_voc_size_t _maximalSize; // 4 bytes
|
||||
TRI_voc_tick_t _fid; // 8 bytes
|
||||
}
|
||||
TRI_df_header_marker_t;
|
||||
|
||||
|
@ -371,10 +374,10 @@ TRI_df_header_marker_t;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct TRI_df_footer_marker_s {
|
||||
TRI_df_marker_t base;
|
||||
TRI_df_marker_t base; // 24 bytes
|
||||
|
||||
TRI_voc_size_t _maximalSize;
|
||||
TRI_voc_size_t _totalSize;
|
||||
TRI_voc_size_t _maximalSize; // 4 bytes
|
||||
TRI_voc_size_t _totalSize; // 4 bytes
|
||||
}
|
||||
TRI_df_footer_marker_t;
|
||||
|
||||
|
@ -383,7 +386,7 @@ TRI_df_footer_marker_t;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct TRI_df_document_marker_s {
|
||||
TRI_df_marker_t base;
|
||||
TRI_df_marker_t base; // 24 bytes
|
||||
}
|
||||
TRI_df_document_marker_t;
|
||||
|
||||
|
@ -392,7 +395,7 @@ TRI_df_document_marker_t;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct TRI_df_skip_marker_s {
|
||||
TRI_df_marker_t base;
|
||||
TRI_df_marker_t base; // 24 bytes
|
||||
}
|
||||
TRI_df_skip_marker_t;
|
||||
|
||||
|
@ -448,6 +451,12 @@ void TRI_FreeDatafile (TRI_datafile_t* datafile);
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief aligns in datafile blocks
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_DF_ALIGN_BLOCK(a) ((((a) + TRI_DF_BLOCK_ALIGNMENT - 1) / TRI_DF_BLOCK_ALIGNMENT) * TRI_DF_BLOCK_ALIGNMENT)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks a CRC of a marker
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1138,7 +1138,7 @@ static int CreateShapedJson (TRI_doc_operation_context_t* context,
|
|||
|
||||
keySize += 1;
|
||||
|
||||
keyBodySize = ((keySize + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
keyBodySize = TRI_DF_ALIGN_BLOCK(keySize);
|
||||
keyBody = TRI_Allocate(TRI_CORE_MEM_ZONE, keyBodySize, true);
|
||||
TRI_CopyString(keyBody, (char*) &keyBuffer, keySize);
|
||||
|
||||
|
@ -1190,7 +1190,7 @@ static int CreateShapedJson (TRI_doc_operation_context_t* context,
|
|||
|
||||
keySize += 1;
|
||||
|
||||
keyBodySize = ((keySize + fromSize + toSize + TRI_DF_BLOCK_ALIGN - 1) / TRI_DF_BLOCK_ALIGN) * TRI_DF_BLOCK_ALIGN;
|
||||
keyBodySize = TRI_DF_ALIGN_BLOCK(keySize + fromSize + toSize);
|
||||
keyBody = TRI_Allocate(TRI_CORE_MEM_ZONE, keyBodySize, true);
|
||||
TRI_CopyString(keyBody, (char*) &keyBuffer, keySize);
|
||||
|
||||
|
|
|
@ -139,6 +139,7 @@ static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary, bool co
|
|||
// create journal file
|
||||
journal = TRI_CreateDatafile(filename, collection->_info._maximalSize);
|
||||
|
||||
|
||||
if (journal == NULL) {
|
||||
if (TRI_errno() == TRI_ERROR_OUT_OF_MEMORY_MMAP) {
|
||||
collection->_lastError = TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY_MMAP);
|
||||
|
@ -149,14 +150,14 @@ static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary, bool co
|
|||
collection->_state = TRI_COL_STATE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
LOG_ERROR("cannot create new journal in '%s'", filename);
|
||||
LOG_ERROR("cannot create new primary journal in '%s'", filename);
|
||||
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
LOG_TRACE("created a new journal '%s'", journal->_filename);
|
||||
LOG_TRACE("created a new primary journal '%s'", journal->_filename);
|
||||
|
||||
// and use the correct name
|
||||
number = TRI_StringUInt32(journal->_fid);
|
||||
|
|
|
@ -62,7 +62,8 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
|
|||
|
||||
jname = TRI_Concatenate3String("journal-", number, ".db");
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, number);
|
||||
|
||||
|
||||
|
||||
filename = TRI_Concatenate2File(collection->base._directory, jname);
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, jname);
|
||||
|
||||
|
@ -79,14 +80,14 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
|
|||
collection->base._state = TRI_COL_STATE_WRITE_ERROR;
|
||||
}
|
||||
|
||||
LOG_ERROR("cannot create new journal '%s': %s", filename, TRI_last_error());
|
||||
LOG_ERROR("cannot create new shape journal '%s': %s", filename, TRI_last_error());
|
||||
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
LOG_TRACE("created a new journal '%s'", journal->_filename);
|
||||
LOG_TRACE("created a new shape journal '%s'", journal->_filename);
|
||||
|
||||
// and use the correct name
|
||||
number = TRI_StringUInt32(journal->_fid);
|
||||
|
@ -100,6 +101,7 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
|
|||
|
||||
if (! ok) {
|
||||
LOG_WARNING("failed to rename the journal to '%s': %s", filename, TRI_last_error());
|
||||
exit(1);
|
||||
}
|
||||
else {
|
||||
LOG_TRACE("renamed journal to '%s'", filename);
|
||||
|
@ -399,6 +401,7 @@ int TRI_WriteShapeCollection (TRI_shape_collection_t* collection,
|
|||
TRI_datafile_t* journal;
|
||||
int res;
|
||||
|
||||
|
||||
// generate a new tick
|
||||
marker->_tick = TRI_NewTickVocBase();
|
||||
|
||||
|
|
|
@ -349,6 +349,7 @@ void TRI_SynchroniserVocBase (void* data) {
|
|||
|
||||
TRI_InitVectorPointer(&collections, TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
|
||||
while (true) {
|
||||
size_t n;
|
||||
size_t i;
|
||||
|
@ -424,6 +425,7 @@ void TRI_SynchroniserVocBase (void* data) {
|
|||
if (state == 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TRI_DestroyVectorPointer(&collections);
|
||||
|
|
|
@ -404,7 +404,6 @@ static TRI_shape_aid_t FindAttributeName (TRI_shaper_t* shaper, char const* name
|
|||
weightedAttribute = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(attribute_weight_t), false);
|
||||
|
||||
if (weightedAttribute != NULL) {
|
||||
|
||||
weightedAttribute->_aid = markerResult->_aid;
|
||||
weightedAttribute->_weight = TRI_VOC_UNDEFINED_ATTRIBUTE_WEIGHT;
|
||||
weightedAttribute->_attribute = (char*)(markerResult) + sizeof(TRI_df_attribute_marker_t);
|
||||
|
@ -569,6 +568,7 @@ static TRI_shape_t const* FindShape (TRI_shaper_t* shaper, TRI_shape_t* shape) {
|
|||
voc_shaper_t* s;
|
||||
void* f;
|
||||
|
||||
|
||||
s = (voc_shaper_t*) shaper;
|
||||
found = TRI_LookupByElementAssociativeSynced(&s->_shapeDictionary, shape);
|
||||
|
||||
|
@ -925,8 +925,10 @@ TRI_shaper_t* TRI_CreateVocShaper (TRI_vocbase_t* vocbase,
|
|||
// override wait for sync for shapes
|
||||
parameter._waitForSync = waitForSync;
|
||||
|
||||
|
||||
collection = TRI_CreateShapeCollection(vocbase, path, ¶meter);
|
||||
|
||||
|
||||
if (collection == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -353,9 +353,21 @@ static bool DropCollectionCallback (TRI_collection_t* col, void* data) {
|
|||
// .............................................................................
|
||||
|
||||
if (collection->_path != NULL) {
|
||||
regcomp(&re, "^(.*)/collection-([0-9][0-9]*)$", REG_EXTENDED);
|
||||
int regExpResult;
|
||||
|
||||
if (regexec(&re, collection->_path, sizeof(matches) / sizeof(matches[0]), matches, 0) == 0) {
|
||||
#ifdef _WIN32
|
||||
// .........................................................................
|
||||
// Just thank your lucky stars that there are only 4 backslashes
|
||||
// .........................................................................
|
||||
regcomp(&re, "^(.*)\\\\collection-([0-9][0-9]*)$", REG_ICASE | REG_EXTENDED);
|
||||
#else
|
||||
regcomp(&re, "^(.*)/collection-([0-9][0-9]*)$", REG_ICASE | REG_EXTENDED);
|
||||
#endif
|
||||
|
||||
|
||||
regExpResult = regexec(&re, collection->_path, sizeof(matches) / sizeof(matches[0]), matches, 0);
|
||||
|
||||
if (regExpResult == 0) {
|
||||
char const* first = collection->_path + matches[1].rm_so;
|
||||
size_t firstLen = matches[1].rm_eo - matches[1].rm_so;
|
||||
|
||||
|
@ -1073,9 +1085,11 @@ bool TRI_msync (int fd, void* mmHandle, char const* begin, char const* end) {
|
|||
uintptr_t g = (intptr_t) PageSize;
|
||||
|
||||
char* b = (char*)( (p / g) * g );
|
||||
char* e = (char*)( ((q + g - 1) / g) * g );
|
||||
char* e = (char*)( ((q + g - 1) / g) * g );
|
||||
int result;
|
||||
|
||||
result = TRI_FlushMMFile(fd, &mmHandle, b, e - b, MS_SYNC);
|
||||
|
||||
int result = TRI_FlushMMFile(&fd, &mmHandle, b, e - b, MS_SYNC);
|
||||
if (result != TRI_ERROR_NO_ERROR) {
|
||||
TRI_set_errno(result);
|
||||
return false;
|
||||
|
@ -1223,6 +1237,7 @@ TRI_vocbase_t* TRI_OpenVocBase (char const* path) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// .............................................................................
|
||||
// vocbase is now active
|
||||
// .............................................................................
|
||||
|
@ -1329,6 +1344,7 @@ void TRI_DestroyVocBase (TRI_vocbase_t* vocbase) {
|
|||
|
||||
// free the filename path
|
||||
TRI_Free(TRI_CORE_MEM_ZONE, vocbase->_path);
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1432,6 +1448,7 @@ TRI_vocbase_col_t* TRI_CreateCollectionVocBase (TRI_vocbase_t* vocbase,
|
|||
assert(parameter);
|
||||
name = parameter->_name;
|
||||
|
||||
|
||||
// check that the name does not contain any strange characters
|
||||
if (! TRI_IsAllowedCollectionName(parameter->_isSystem, name)) {
|
||||
TRI_set_errno(TRI_ERROR_ARANGO_ILLEGAL_NAME);
|
||||
|
@ -1480,13 +1497,15 @@ TRI_vocbase_col_t* TRI_CreateCollectionVocBase (TRI_vocbase_t* vocbase,
|
|||
|
||||
col = &primary->base;
|
||||
|
||||
// add collection container
|
||||
|
||||
// add collection container -- however note that the compactor is added later which could fail!
|
||||
collection = AddCollection(vocbase,
|
||||
col->_info._type,
|
||||
col->_info._name,
|
||||
col->_info._cid,
|
||||
col->_directory);
|
||||
|
||||
|
||||
if (collection == NULL) {
|
||||
if (TRI_IS_DOCUMENT_COLLECTION(type)) {
|
||||
TRI_CloseDocumentCollection(document);
|
||||
|
@ -1497,11 +1516,13 @@ TRI_vocbase_col_t* TRI_CreateCollectionVocBase (TRI_vocbase_t* vocbase,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
collection->_status = TRI_VOC_COL_STATUS_LOADED;
|
||||
collection->_collection = primary;
|
||||
FreeCollectionPath(collection);
|
||||
collection->_path = TRI_DuplicateString(primary->base._directory);
|
||||
|
||||
|
||||
TRI_WRITE_UNLOCK_COLLECTIONS_VOCBASE(vocbase);
|
||||
return collection;
|
||||
}
|
||||
|
@ -1919,7 +1940,9 @@ void TRI_InitialiseVocBase () {
|
|||
#ifdef TRI_ICU_VERSION
|
||||
LOG_TRACE("%s", "$Revision: ICU " TRI_ICU_VERSION " $");
|
||||
#endif
|
||||
|
||||
|
||||
LOG_TRACE("sizeof df_header: %d", (int) sizeof(TRI_df_marker_t));
|
||||
LOG_TRACE("sizeof df_header_marker: %d", (int) sizeof(TRI_df_header_marker_t));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -149,9 +149,9 @@ namespace triagens {
|
|||
}
|
||||
parser._dataAdd = this;
|
||||
|
||||
char buffer[10240];
|
||||
char buffer[16384];
|
||||
|
||||
while (!_hasError) {
|
||||
while (! _hasError) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
ssize_t n = read(fd, buffer, sizeof(buffer));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief V8 shell
|
||||
///
|
||||
/// @file
|
||||
|
@ -985,10 +984,10 @@ static bool RunScripts (v8::Handle<v8::Context> context,
|
|||
}
|
||||
|
||||
if (execute) {
|
||||
TRI_ExecuteJavaScriptFile(context, scripts[i].c_str());
|
||||
TRI_ExecuteLocalJavaScriptFile(scripts[i].c_str());
|
||||
}
|
||||
else {
|
||||
TRI_LoadJavaScriptFile(context, scripts[i].c_str());
|
||||
TRI_ParseJavaScriptFile(scripts[i].c_str());
|
||||
}
|
||||
|
||||
if (tryCatch.HasCaught()) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#ifdef _WIN32
|
||||
#include "build_win.h"
|
||||
#else
|
||||
#include "build_posix.h"
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
#define TRIAGENS_VERSION "1.1.1"
|
|
@ -0,0 +1,13 @@
|
|||
#ifdef _WIN64
|
||||
#ifdef _DEBUG
|
||||
#define TRIAGENS_VERSION "1.1.1 [WIN64-DEBUG BETA]"
|
||||
#else
|
||||
#define TRIAGENS_VERSION "1.1.1 [WIN64-RELEASE BETA]"
|
||||
#endif
|
||||
#else
|
||||
#ifdef _DEBUG
|
||||
#define TRIAGENS_VERSION "1.1.1 [WIN32-DEBUG BETA]"
|
||||
#else
|
||||
#define TRIAGENS_VERSION "1.1.1 [WIN32-RELEASE BETA]"
|
||||
#endif
|
||||
#endif
|
|
@ -43,8 +43,9 @@ def genJsFile(errors):
|
|||
msg = e[2].replace("\n", " ").replace("\\", "").replace("\"", "\\\"")
|
||||
out = out\
|
||||
+ " " + name.ljust(30) + " : { \"code\" : " + e[1] + ", \"message\" : \"" + msg + "\" }"
|
||||
|
||||
|
||||
i = i + 1
|
||||
|
||||
if i < len(errors):
|
||||
out = out + ", \n"
|
||||
else:
|
||||
|
|
|
@ -193,7 +193,7 @@ dnl ============================================================================
|
|||
dnl --SECTION-- GENERATE FILES
|
||||
dnl ============================================================================
|
||||
|
||||
BUILD_H="\$(top_srcdir)/build.h"
|
||||
BUILD_H="\$(top_srcdir)/build_posix.h"
|
||||
AC_SUBST(BUILD_H)
|
||||
|
||||
AC_CONFIG_FILES([Makefile Documentation/arango.template])
|
||||
|
|
|
@ -103,7 +103,6 @@ function CollectionEdgeSuiteErrorHandling () {
|
|||
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, err.errorNum);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
edge.save(v1, "1234/56/46", {});
|
||||
fail();
|
||||
|
@ -431,44 +430,6 @@ function CollectionEdgeSuite () {
|
|||
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, err.errorNum);
|
||||
}
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test rollback of edge insert
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRollbackEdgeInsert : function () {
|
||||
var v1 = vertex.save({ "x" : 1 });
|
||||
var v2 = vertex.save({ "x" : 2 });
|
||||
|
||||
edge.ensureUniqueConstraint("myid");
|
||||
edge.save(v1, v2, { "myid" : 1 });
|
||||
try {
|
||||
edge.save(v1, v2, { "myid" : 1 });
|
||||
}
|
||||
catch (err) {
|
||||
assertEqual(ERRORS.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED.code, err.errorNum);
|
||||
}
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test rollback of edge update
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRollbackEdgeUpdate : function () {
|
||||
var v1 = vertex.save({ "x" : 1 });
|
||||
var v2 = vertex.save({ "x" : 2 });
|
||||
|
||||
edge.ensureUniqueConstraint("myid");
|
||||
edge.save(v1, v2, { "myid" : 1 });
|
||||
var e2 = edge.save(v1, v2, { "myid" : 2 });
|
||||
|
||||
try {
|
||||
edge.update(e2, { "myid" : 1 });
|
||||
}
|
||||
catch (err) {
|
||||
assertEqual(ERRORS.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED.code, err.errorNum);
|
||||
}
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief invalid collection type for edges
|
||||
|
|
|
@ -331,6 +331,10 @@ function main (argv) {
|
|||
while (true) {
|
||||
var line = console.getline();
|
||||
|
||||
if (line == "") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (line === "*") {
|
||||
for (i = 0; i < collections.length; ++i) {
|
||||
a.push(i);
|
||||
|
@ -341,7 +345,7 @@ function main (argv) {
|
|||
else {
|
||||
var l = parseInt(line);
|
||||
|
||||
if (l < 0 || l >= collections.length) {
|
||||
if (l < 0 || l >= collections.length || l === null || l === undefined || isNaN(l)) {
|
||||
printf("Please select a number between 0 and %d: ", collections.length - 1);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/I"..\..\lib"
|
||||
/I"..\"
|
||||
/I"..\..\"
|
||||
/I"..\..\VisualStudio"
|
||||
/I"..\..\3rdParty\VisualStudio\openssl\x86\include"
|
||||
/I"..\..\3rdParty\VisualStudio\mygetopt"
|
||||
/I"..\..\3rdParty\VisualStudio\regex-2.7\src"
|
||||
/I"..\..\3rdParty\VisualStudio\readline-5.0-1-lib\include"
|
||||
/I"..\..\arangod"
|
||||
/I"..\..\3rdParty\VisualStudio\V8\include"
|
||||
/Zi (debug information format)
|
||||
/nologo (suppress startup banner)
|
||||
/W3 (warnings)
|
||||
/WX- (warnings)
|
||||
|
||||
/O2 (optimise)
|
||||
/Oi
|
||||
/Oy-
|
||||
|
||||
|
||||
/GL
|
||||
/D "WIN32"
|
||||
/D "NDEBUG"
|
||||
/D "_CONSOLE"
|
||||
/D "_WIN32"
|
||||
/D "YY_NO_UNISTD_H"
|
||||
/D "_UNICODE"
|
||||
/D "UNICODE"
|
||||
/D ")"
|
||||
|
||||
|
||||
/Gm-
|
||||
/EHsc
|
||||
|
||||
|
||||
/MT
|
||||
/GS !!!!!!!!!!!!!!
|
||||
/Gy ?????????????
|
||||
|
||||
/fp:precise
|
||||
/Zc:wchar_t ???????????????????
|
||||
/Zc:forScope !!!!!!!!!!!!!!!!!!
|
||||
|
||||
/Gd ???????????????????
|
||||
/analyze-
|
||||
/errorReport:queue
|
||||
|
||||
/Fp"Release\arangod.pch"
|
||||
/Fa"Release\"
|
||||
/Fo"Release\"
|
||||
/Fd"Release\vc100.pdb"
|
|
@ -95,11 +95,14 @@
|
|||
if (FS_EXISTS(versionFile)) {
|
||||
// VERSION file exists, read its contents
|
||||
var versionInfo = SYS_READ(versionFile);
|
||||
|
||||
if (versionInfo != '') {
|
||||
var versionValues = JSON.parse(versionInfo);
|
||||
|
||||
if (versionValues && versionValues.version && ! isNaN(versionValues.version)) {
|
||||
lastVersion = parseFloat(versionValues.version);
|
||||
}
|
||||
|
||||
if (versionValues && versionValues.tasks && typeof(versionValues.tasks) === 'object') {
|
||||
lastTasks = versionValues.tasks || { };
|
||||
}
|
||||
|
|
|
@ -284,7 +284,6 @@ void ApplicationServer::setupLogging () {
|
|||
// the user specified a log file to use but it could not be created. bail out
|
||||
std::cerr << "failed to create logfile '" << _logFile << "'. Please check the path and permissions." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
}
|
||||
}
|
||||
#ifdef TRI_ENABLE_SYSLOG
|
||||
|
@ -384,6 +383,7 @@ bool ApplicationServer::parse (int argc,
|
|||
// UID and GID
|
||||
// .............................................................................
|
||||
|
||||
|
||||
storeRealPrivileges();
|
||||
extractPrivileges();
|
||||
dropPrivileges();
|
||||
|
@ -474,7 +474,10 @@ bool ApplicationServer::parse (int argc,
|
|||
|
||||
void ApplicationServer::prepare () {
|
||||
|
||||
// prepare all features
|
||||
// prepare all features - why reverse?
|
||||
|
||||
// reason: features might depend on each other. by creating them in reverse order and shutting them
|
||||
// down in forward order, we ensure that the features created last are destroyed first, i.e.: LIFO
|
||||
for (vector<ApplicationFeature*>::reverse_iterator i = _features.rbegin(); i != _features.rend(); ++i) {
|
||||
ApplicationFeature* feature = *i;
|
||||
|
||||
|
@ -575,13 +578,11 @@ void ApplicationServer::wait () {
|
|||
|
||||
// wait until we receive a stop signal
|
||||
while (running && _stopping == 0) {
|
||||
|
||||
// check the parent and wait for a second
|
||||
if (! checkParent()) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
@ -594,7 +595,6 @@ void ApplicationServer::beginShutdown () {
|
|||
if (_stopping != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_stopping = 1;
|
||||
}
|
||||
|
||||
|
@ -605,6 +605,7 @@ void ApplicationServer::beginShutdown () {
|
|||
void ApplicationServer::stop () {
|
||||
beginShutdown();
|
||||
|
||||
|
||||
// close all features
|
||||
for (vector<ApplicationFeature*>::iterator i = _features.begin(); i != _features.end(); ++i) {
|
||||
ApplicationFeature* feature = *i;
|
||||
|
@ -614,6 +615,7 @@ void ApplicationServer::stop () {
|
|||
LOGGER_TRACE << "closed server feature '" << feature->getName() << "'";
|
||||
}
|
||||
|
||||
|
||||
// stop all features
|
||||
for (vector<ApplicationFeature*>::reverse_iterator i = _features.rbegin(); i != _features.rend(); ++i) {
|
||||
ApplicationFeature* feature = *i;
|
||||
|
@ -624,6 +626,7 @@ void ApplicationServer::stop () {
|
|||
|
||||
LOGGER_TRACE << "shut down server feature '" << feature->getName() << "'";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -901,7 +904,8 @@ bool ApplicationServer::readConfigurationFile () {
|
|||
LOGGER_DEBUG << "no init file has been specified";
|
||||
}
|
||||
|
||||
// nothing has been specified on the command line regarding configuration file
|
||||
|
||||
// nothing has been specified on the command line regarding the user's configuration file
|
||||
if (! _userConfigFile.empty()) {
|
||||
|
||||
// .........................................................................
|
||||
|
@ -975,7 +979,9 @@ bool ApplicationServer::readConfigurationFile () {
|
|||
return ok;
|
||||
}
|
||||
else {
|
||||
|
||||
LOGGER_INFO << "no user init file '" << homeDir << "' found";
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -983,6 +989,7 @@ bool ApplicationServer::readConfigurationFile () {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (_systemConfigPath.empty()) {
|
||||
|
||||
#ifdef _SYSCONFDIR_
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
|
||||
#include "BasicsC/common.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "BasicsC/win-utils.h"
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup Configuration Configuration
|
||||
/// @{
|
||||
|
|
|
@ -328,8 +328,14 @@ namespace triagens {
|
|||
bool isSymbolicLink (string const& path) {
|
||||
|
||||
#ifdef TRI_HAVE_WIN32_SYMBOLIC_LINK
|
||||
|
||||
return true;
|
||||
|
||||
// .........................................................................
|
||||
// TODO: On the NTFS file system, there are the following file links:
|
||||
// hard links -
|
||||
// junctions -
|
||||
// symbolic links -
|
||||
// .........................................................................
|
||||
return false;
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -245,8 +245,10 @@ namespace RandomHelper {
|
|||
#else
|
||||
public:
|
||||
RandomDeviceWin32 () : cryptoHandle(0), pos(0) {
|
||||
CryptAcquireContext(&cryptoHandle, NULL, NULL, PROV_RSA_FULL, 0);
|
||||
if (cryptoHandle == 0) {
|
||||
BOOL result;
|
||||
result = CryptAcquireContext(&cryptoHandle, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
|
||||
if (cryptoHandle == 0 || result == FALSE) {
|
||||
printf("%s:%s:%d:%d",__FILE__,__FUNCTION__,__LINE__,GetLastError());
|
||||
THROW_INTERNAL_ERROR("cannot create cryptographic windows handle");
|
||||
}
|
||||
fillBuffer();
|
||||
|
@ -255,8 +257,8 @@ namespace RandomHelper {
|
|||
|
||||
~RandomDeviceWin32 () {
|
||||
if (cryptoHandle != 0) {
|
||||
CryptReleaseContext(cryptoHandle, 0);
|
||||
}
|
||||
CryptReleaseContext(cryptoHandle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -274,8 +276,8 @@ namespace RandomHelper {
|
|||
BYTE* ptr = reinterpret_cast<BYTE*>(&buffer);
|
||||
|
||||
// fill the buffer with random characters
|
||||
int result = CryptGenRandom(cryptoHandle, n, ptr);
|
||||
if (result == 0) {
|
||||
int result = CryptGenRandom(cryptoHandle, n, ptr);
|
||||
if (result == 0) {
|
||||
LOGGER_FATAL << "read on random device failed: nothing read";
|
||||
THROW_INTERNAL_ERROR("read on random device failed");
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private:
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief read-write lock variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -3197,6 +3197,14 @@ namespace triagens {
|
|||
// ADDITIONAL STRING UTILITIES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
string correctPath(const string& incorrectPath) {
|
||||
#ifdef _WIN32
|
||||
return replace (incorrectPath, "/", "\\");
|
||||
#else
|
||||
return replace (incorrectPath, "\\", "/");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// In a list str = "xx,yy,zz ...", entry(n,str,',') returns the nth entry of the list delimited
|
||||
/// by ','. E.g entry(2,str,',') = 'yy'
|
||||
|
||||
|
|
|
@ -740,6 +740,13 @@ namespace triagens {
|
|||
// ADDITIONAL STRING UTILITIES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @ingroup Utilities
|
||||
/// @brief replaces incorrect path delimiter character for window and linux
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string correctPath (const string& incorrectPath);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @ingroup Utilities
|
||||
/// @brief finds n.th entry
|
||||
|
|
|
@ -105,15 +105,24 @@
|
|||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TRI_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// .................................................................................................
|
||||
// The problem we have for visual studio is that if we include WinSock2.h here it may conflict later
|
||||
// in some other source file. The conflict arises when windows.h is included BEFORE WinSock2.h --
|
||||
// this is a visual studio issue. For now be VERY careful to ensure that if you need windows.h, then
|
||||
// you include this file AFTER common.h.
|
||||
// .................................................................................................
|
||||
|
||||
#ifdef TRI_HAVE_WINSOCK2_H
|
||||
#include <WinSock2.h>
|
||||
typedef long suseconds_t;
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -523,8 +523,20 @@ TRI_vector_string_t TRI_FilesDirectory (char const* path) {
|
|||
|
||||
int TRI_RenameFile (char const* old, char const* filename) {
|
||||
int res;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL moveResult = 0;
|
||||
moveResult = MoveFileExA(old, filename, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
|
||||
if (!moveResult) {
|
||||
DWORD errorCode = GetLastError();
|
||||
res = -1;
|
||||
}
|
||||
else {
|
||||
res = 0;
|
||||
}
|
||||
#else
|
||||
res = rename(old, filename);
|
||||
#endif
|
||||
|
||||
if (res != 0) {
|
||||
LOG_TRACE("cannot rename file from '%s' to '%s': %s", old, filename, TRI_LAST_ERROR_STR);
|
||||
|
@ -560,9 +572,11 @@ bool TRI_ReadPointer (int fd, void* buffer, size_t length) {
|
|||
|
||||
ptr = buffer;
|
||||
|
||||
|
||||
while (0 < length) {
|
||||
ssize_t n = TRI_READ(fd, ptr, length);
|
||||
|
||||
|
||||
if (n < 0) {
|
||||
TRI_set_errno(TRI_ERROR_SYS_ERROR);
|
||||
LOG_ERROR("cannot read: %s", TRI_LAST_ERROR_STR);
|
||||
|
@ -995,6 +1009,7 @@ int TRI_DestroyLockFile (char const* filename) {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
fd = TRI_OPEN(filename, O_RDWR);
|
||||
|
||||
// ..........................................................................
|
||||
|
@ -1063,20 +1078,110 @@ int TRI_DestroyLockFile (char const* filename) {
|
|||
/// for those cases.
|
||||
/// It is the caller's responsibility to free the string created by this
|
||||
/// function
|
||||
/// TODO: the implementation is naive and may be simplified greatly on Windows
|
||||
/// by using GetFullPathName()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _WIN32
|
||||
char* TRI_GetAbsolutePath (char const* fileName, char const* currentWorkingDirectory) {
|
||||
char* result;
|
||||
char* ptr;
|
||||
size_t cwdLength;
|
||||
size_t fileLength;
|
||||
bool ok;
|
||||
|
||||
// ...........................................................................
|
||||
// Check that fileName actually makes some sense
|
||||
// ...........................................................................
|
||||
|
||||
if (fileName == NULL || *fileName == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Under windows we can assume that fileName is absolute if fileName starts
|
||||
// with a letter A-Z followed by a colon followed by either a forward or
|
||||
// backslash.
|
||||
// ...........................................................................
|
||||
|
||||
if ((fileName[0] > 64 && fileName[0] < 91) || (fileName[0] > 96 && fileName[0] < 123)) {
|
||||
if ((fileName[1] != '\0') && (fileName[1] == ':')) {
|
||||
if ((fileName[2] != '\0') && (fileName[2] == '/' || fileName[2] == '\\')) {
|
||||
return TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// The fileName itself was not absolute, so we attempt to amalagmate the
|
||||
// currentWorkingDirectory with the fileName
|
||||
// ...........................................................................
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Check that the currentWorkingDirectory makes sense
|
||||
// ...........................................................................
|
||||
|
||||
if (currentWorkingDirectory == NULL || *currentWorkingDirectory == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Under windows the currentWorkingDirectory should start
|
||||
// with a letter A-Z followed by a colon followed by either a forward or
|
||||
// backslash.
|
||||
// ...........................................................................
|
||||
|
||||
ok = false;
|
||||
if ((currentWorkingDirectory[0] > 64 && currentWorkingDirectory[0] < 91) || (currentWorkingDirectory[0] > 96 && currentWorkingDirectory[0] < 123)) {
|
||||
if ((currentWorkingDirectory[1] != '\0') && (currentWorkingDirectory[1] == ':')) {
|
||||
if ((currentWorkingDirectory[2] != '\0') && (currentWorkingDirectory[2] == '/' || currentWorkingDirectory[2] == '\\')) {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Determine the total legnth of the new string
|
||||
// ...........................................................................
|
||||
|
||||
cwdLength = strlen(currentWorkingDirectory);
|
||||
fileLength = strlen(fileName);
|
||||
|
||||
if (currentWorkingDirectory[cwdLength - 1] != '\\' && currentWorkingDirectory[cwdLength - 1] != '/') {
|
||||
// we do not require a backslash
|
||||
result = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, (cwdLength + fileLength + 1) * sizeof(char), false);
|
||||
memcpy(result, currentWorkingDirectory, cwdLength);
|
||||
memcpy(result + cwdLength, fileName, fileLength);
|
||||
result[cwdLength + fileLength] = '\0';
|
||||
}
|
||||
else {
|
||||
// we do require a backslash
|
||||
result = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, (cwdLength + fileLength + 2) * sizeof(char), false);
|
||||
memcpy(result, currentWorkingDirectory, cwdLength);
|
||||
result[cwdLength] = '\\';
|
||||
memcpy(result + cwdLength + 1, fileName, fileLength);
|
||||
result[cwdLength + fileLength + 1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
char* TRI_GetAbsolutePath (char const* file, char const* cwd) {
|
||||
char* result;
|
||||
char* ptr;
|
||||
size_t cwdLength;
|
||||
bool isAbsolute;
|
||||
|
||||
#ifdef _WIN32
|
||||
#error please validate if this works on Windows
|
||||
#endif
|
||||
|
||||
if (file == NULL || *file == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1110,15 +1215,9 @@ char* TRI_GetAbsolutePath (char const* file, char const* cwd) {
|
|||
memcpy(ptr, cwd, cwdLength);
|
||||
ptr += cwdLength;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (cwd[cwdLength - 1] != '\\') {
|
||||
*(ptr++) = '\\';
|
||||
}
|
||||
#else
|
||||
if (cwd[cwdLength - 1] != '/') {
|
||||
*(ptr++) = '/';
|
||||
}
|
||||
#endif
|
||||
memcpy(ptr, file, strlen(file));
|
||||
ptr += strlen(file);
|
||||
*ptr = '\0';
|
||||
|
@ -1126,6 +1225,7 @@ char* TRI_GetAbsolutePath (char const* file, char const* cwd) {
|
|||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief locates the directory containing the program
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
#ifndef TRIAGENS_BASICS_C_FILES_H
|
||||
#define TRIAGENS_BASICS_C_FILES_H 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "BasicsC/win-utils.h"
|
||||
#endif
|
||||
|
||||
#include "BasicsC/common.h"
|
||||
|
||||
#include "BasicsC/vector.h"
|
||||
|
@ -49,7 +53,7 @@ extern "C" {
|
|||
/// @brief sets close-on-exit for a socket
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TRI_SetCloseOnExecFile (int fd);
|
||||
bool TRI_SetCloseOnExecFile (socket_t fd);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns the size of a file
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#include "BasicsC/files.h"
|
||||
#include "BasicsC/logging.h"
|
||||
#include "BasicsC/string-buffer.h"
|
||||
|
|
|
@ -355,7 +355,7 @@ void TRI_WriteLockReadWriteLock (TRI_read_write_lock_t* lock) {
|
|||
rc = pthread_rwlock_wrlock(lock);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_ERROR("could not read-lock the read-write lock: %s", strerror(rc));
|
||||
LOG_ERROR("could not write-lock the read-write lock: %s", strerror(rc));
|
||||
if (rc == EDEADLK) {
|
||||
LOG_ERROR("rw-lock deadlock detected");
|
||||
}
|
||||
|
|
|
@ -186,18 +186,41 @@ void TRI_UnlockSpin (TRI_spin_t* spin) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_InitReadWriteLock (TRI_read_write_lock_t* lock) {
|
||||
|
||||
// ...........................................................................
|
||||
// set the number of readers reading on the read_write lock to 0
|
||||
// ...........................................................................
|
||||
|
||||
lock->_readers = 0;
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Signaled: writer has no access
|
||||
// Non-Signaled: writer has access, block readers
|
||||
// Creates an event which allows a thread to wait on it (perhaps should use
|
||||
// a mutux rather than an event here). The writer event is set to signalled
|
||||
// when CreateEvent is called with these parameters.
|
||||
// ...........................................................................
|
||||
|
||||
lock->_writerEvent = CreateEvent(0, TRUE, TRUE, 0);
|
||||
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Signaled: no readers
|
||||
// Non-Signaled: some readers have access, block writer
|
||||
// Same as the writer event above except this is the reader event
|
||||
// ...........................................................................
|
||||
|
||||
lock->_readersEvent = CreateEvent(0, TRUE, TRUE, 0);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Creates critical sections for writer and readers.
|
||||
// Waits for ownership of the specified critical section object.
|
||||
// The function returns when the calling thread is granted ownership.
|
||||
// ...........................................................................
|
||||
|
||||
InitializeCriticalSection(&lock->_lockWriter);
|
||||
InitializeCriticalSection(&lock->_lockReaders);
|
||||
}
|
||||
|
@ -232,8 +255,18 @@ void TRI_DestroyReadWriteLock (TRI_read_write_lock_t* lock) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void IncrementReaders (TRI_read_write_lock_t* lock) {
|
||||
|
||||
// ...........................................................................
|
||||
// increment the number of readers we have on the read_write lock
|
||||
// ...........................................................................
|
||||
|
||||
lock->_readers++;
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Since the number of readers must be positive, set the readers event to
|
||||
// non-signalled so that any write event will have to wait.
|
||||
// ...........................................................................
|
||||
ResetEvent(lock->_readersEvent);
|
||||
}
|
||||
|
||||
|
@ -242,8 +275,19 @@ static void IncrementReaders (TRI_read_write_lock_t* lock) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void DecrementReaders (TRI_read_write_lock_t* lock) {
|
||||
|
||||
// ...........................................................................
|
||||
// reduce the number of readers using the read_write lock by 1
|
||||
// ...........................................................................
|
||||
|
||||
lock->_readers--;
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// When the number of readers is 0, set the event to signalled which allows
|
||||
// a writer to use the read_write lock.
|
||||
// ...........................................................................
|
||||
|
||||
if (lock->_readers == 0) {
|
||||
SetEvent(lock->_readersEvent);
|
||||
}
|
||||
|
@ -292,15 +336,35 @@ bool TRI_TryReadLockReadWriteLock (TRI_read_write_lock_t* lock) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_ReadLockReadWriteLock (TRI_read_write_lock_t* lock) {
|
||||
|
||||
while (true) {
|
||||
|
||||
// ........................................................................
|
||||
// Waits for a writer to finish if there is one. This function only
|
||||
// returns when the writer event is in a signalled state
|
||||
// ........................................................................
|
||||
|
||||
WaitForSingleObject(lock->_writerEvent, INFINITE);
|
||||
|
||||
|
||||
// .........................................................................
|
||||
// This thread will wait here until this resource becomes excusively available
|
||||
// .........................................................................
|
||||
|
||||
EnterCriticalSection(&lock->_lockReaders);
|
||||
IncrementReaders(lock);
|
||||
|
||||
// .........................................................................
|
||||
// allows some other thread to use this resource
|
||||
// .........................................................................
|
||||
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
|
||||
|
||||
// it could have happened that the writer event is no longer in a signalled
|
||||
// state. Between leaving the crtical section and here a writer sneaked in.
|
||||
//
|
||||
if (WaitForSingleObject(lock->_writerEvent, 0) != WAIT_OBJECT_0) {
|
||||
exit(EXIT_FAILURE);
|
||||
EnterCriticalSection(&lock->_lockReaders);
|
||||
DecrementReaders(lock);
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
|
@ -318,12 +382,13 @@ void TRI_ReadLockReadWriteLock (TRI_read_write_lock_t* lock) {
|
|||
void TRI_ReadUnlockReadWriteLock (TRI_read_write_lock_t* lock) {
|
||||
EnterCriticalSection(&lock->_lockReaders);
|
||||
|
||||
/* this is wrong since it is possible for the write locker to block this event
|
||||
// a write lock eists
|
||||
if (WaitForSingleObject(lock->_writerEvent, 0) != WAIT_OBJECT_0) {
|
||||
LOG_FATAL("write lock, but trying to unlock read");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
// at least one reader exists
|
||||
else if (0 < lock->_readers) {
|
||||
DecrementReaders(lock);
|
||||
|
@ -331,14 +396,28 @@ void TRI_ReadUnlockReadWriteLock (TRI_read_write_lock_t* lock) {
|
|||
|
||||
// ups, no writer and no reader
|
||||
else {
|
||||
LeaveCriticalSection(&lock->_lockWriter);
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
LOG_FATAL("no reader and no writer, but trying to unlock");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*/
|
||||
|
||||
if (0 < lock->_readers) {
|
||||
DecrementReaders(lock);
|
||||
}
|
||||
|
||||
// oops no reader
|
||||
else {
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
LOG_FATAL("no reader, but trying to unlock read lock");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief tries to write lock a read-write lock
|
||||
/// TODO: not yet implemented
|
||||
|
@ -353,44 +432,101 @@ bool TRI_TryWriteLockReadWriteLock (TRI_read_write_lock_t* lock) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_WriteLockReadWriteLock (TRI_read_write_lock_t* lock) {
|
||||
|
||||
// ...........................................................................
|
||||
// Lock so no other thread can access this
|
||||
// EnterCriticalSection(&lock->_lockWriter) will block this thread until
|
||||
// it has been released by the other thread.
|
||||
// ...........................................................................
|
||||
|
||||
EnterCriticalSection(&lock->_lockWriter);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Wait until the lock->_writerEvent is in a 'signalled' state
|
||||
// ...........................................................................
|
||||
|
||||
WaitForSingleObject(lock->_writerEvent, INFINITE);
|
||||
|
||||
ResetEvent(lock->_writerEvent);
|
||||
|
||||
// ...........................................................................
|
||||
// Set _writeEvent as nonsignalled -- this will block other read/write
|
||||
// lockers
|
||||
// ...........................................................................
|
||||
|
||||
ResetEvent(lock->_writerEvent);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// If there are ANY read locks outstanding, then wait until these are cleared
|
||||
// ...........................................................................
|
||||
|
||||
WaitForSingleObject(lock->_readersEvent, INFINITE);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Allow other threads to access this function
|
||||
// ...........................................................................
|
||||
|
||||
LeaveCriticalSection(&lock->_lockWriter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief write unlocks read-write lock
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_WriteUnlockReadWriteLock (TRI_read_write_lock_t* lock) {
|
||||
//printf("%s:%s:%s:%d:%s:%d\n","oreste",__FILE__,__FUNCTION__,__LINE__,"!!!!!!!!!!!!!!!!!",(uint64_t)(lock));
|
||||
|
||||
// ...........................................................................
|
||||
// Write lock this _lockReader so no other threads can access this
|
||||
// This will block this thread until it is released by the other thread
|
||||
// We do not need to lock the _lockWriter SINCE the TRI_WriteLockReadWriteLock
|
||||
// function above will lock (due to the ResetEvent(lock->_writerEvent); )
|
||||
// ...........................................................................
|
||||
|
||||
EnterCriticalSection(&lock->_lockReaders);
|
||||
|
||||
// a write lock eists
|
||||
|
||||
// ...........................................................................
|
||||
// In the function TRI_WriteLockReadWriteLock we set the _writerEvent to
|
||||
// 'nonsignalled'. So if a write lock exists clear it by setting it to
|
||||
// 'signalled'
|
||||
// ...........................................................................
|
||||
|
||||
if (WaitForSingleObject(lock->_writerEvent, 0) != WAIT_OBJECT_0) {
|
||||
SetEvent(lock->_writerEvent);
|
||||
}
|
||||
|
||||
// at least one reader exists
|
||||
// ...........................................................................
|
||||
// Oops at least one reader exists - something terrible happened.
|
||||
// ...........................................................................
|
||||
|
||||
else if (0 < lock->_readers) {
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
LOG_FATAL("read lock, but trying to unlock write");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// ups, no writer and no reader
|
||||
|
||||
// ...........................................................................
|
||||
// Oops we are trying to unlock a write lock, but there isn't one! Something
|
||||
// terrible happend.
|
||||
// ...........................................................................
|
||||
|
||||
else {
|
||||
LeaveCriticalSection(&lock->_lockWriter);
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
LOG_FATAL("no reader and no writer, but trying to unlock");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&lock->_lockWriter);
|
||||
|
||||
// ...........................................................................
|
||||
// Allow read locks to be applied now.
|
||||
// ...........................................................................
|
||||
|
||||
LeaveCriticalSection(&lock->_lockReaders);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -596,7 +732,7 @@ void TRI_WaitCondition (TRI_condition_t* cond) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief waits for a signal with a timeout in milli-seconds
|
||||
/// @brief waits for a signal with a timeout in micro-seconds
|
||||
///
|
||||
/// Note that you must hold the lock.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -605,6 +741,13 @@ bool TRI_TimedWaitCondition (TRI_condition_t* cond, uint64_t delay) {
|
|||
bool lastWaiter;
|
||||
DWORD res;
|
||||
|
||||
// ...........................................................................
|
||||
// The POSIX threads function pthread_cond_timedwait accepts microseconds
|
||||
// while the the function SignalObjectAndWait accepts milliseconds
|
||||
// ...........................................................................
|
||||
|
||||
delay = delay / 1000;
|
||||
|
||||
// avoid race conditions
|
||||
EnterCriticalSection(&cond->_lockWaiters);
|
||||
cond->_waiters++;
|
||||
|
|
|
@ -393,10 +393,12 @@ static int GenerateMessage (char* buffer,
|
|||
n = 0;
|
||||
TRI_LockSpin(&OutputPrefixLock);
|
||||
|
||||
|
||||
if (OutputPrefix && *OutputPrefix) {
|
||||
n = snprintf(buffer, size, "%s ", OutputPrefix);
|
||||
}
|
||||
|
||||
|
||||
TRI_UnlockSpin(&OutputPrefixLock);
|
||||
|
||||
if (n < 0) {
|
||||
|
@ -515,6 +517,7 @@ static void OutputMessage (TRI_log_level_e level,
|
|||
size_t length,
|
||||
bool copy) {
|
||||
size_t i;
|
||||
|
||||
|
||||
if (! LoggingActive) {
|
||||
if (! copy) {
|
||||
|
@ -689,10 +692,12 @@ static void LogThread (char const* func,
|
|||
// write time in buffer
|
||||
len = strftime(buffer, 32, "%Y-%m-%dT%H:%M:%SZ ", &tb);
|
||||
|
||||
|
||||
va_copy(ap2, ap);
|
||||
n = GenerateMessage(buffer + len, sizeof(buffer) - len, func, file, line, level, processId, threadId, fmt, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
|
||||
if (n == -1) {
|
||||
TRI_Log(func, file, line, TRI_LOG_LEVEL_WARNING, TRI_LOG_SEVERITY_HUMAN, "format string is corrupt");
|
||||
return;
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
// Flush memory mapped file to disk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_FlushMMFile(void* fileHandle, void** mmHandle, void* startingAddress, size_t numOfBytesToFlush, int flags) {
|
||||
int TRI_FlushMMFile(int fileDescriptor, void** mmHandle, void* startingAddress, size_t numOfBytesToFlush, int flags) {
|
||||
|
||||
// ...........................................................................
|
||||
// Possible flags to send are (based upon the Ubuntu Linux ASM include files:
|
||||
|
@ -69,12 +69,10 @@ int TRI_FlushMMFile(void* fileHandle, void** mmHandle, void* startingAddress, si
|
|||
|
||||
#ifdef __APPLE__
|
||||
if (res == 0) {
|
||||
if (fileHandle != NULL) {
|
||||
int fd = *((int*)(fileHandle));
|
||||
res = fcntl(fd, F_FULLFSYNC, 0);
|
||||
}
|
||||
res = fcntl(fileDescriptor, F_FULLFSYNC, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (res == 0) {
|
||||
// msync was successful
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -88,7 +86,7 @@ int TRI_FlushMMFile(void* fileHandle, void** mmHandle, void* startingAddress, si
|
|||
|
||||
return TRI_ERROR_ARANGO_MSYNC_FAILED;
|
||||
}
|
||||
|
||||
|
||||
return TRI_ERROR_SYS_ERROR;
|
||||
}
|
||||
|
||||
|
@ -96,21 +94,16 @@ int TRI_MMFile(void* memoryAddress,
|
|||
size_t numOfBytesToInitialise,
|
||||
int memoryProtection,
|
||||
int flags,
|
||||
void* fileHandle,
|
||||
int fileDescriptor,
|
||||
void** mmHandle,
|
||||
int64_t offset,
|
||||
void** result) {
|
||||
|
||||
int fd = -1;
|
||||
off_t offsetRetyped = (off_t)(offset);
|
||||
|
||||
*mmHandle = NULL; // only useful for windows
|
||||
|
||||
if (fileHandle != NULL) {
|
||||
fd = *((int*)(fileHandle));
|
||||
}
|
||||
|
||||
*result = mmap(memoryAddress, numOfBytesToInitialise, memoryProtection, flags, fd, offsetRetyped);
|
||||
*result = mmap(memoryAddress, numOfBytesToInitialise, memoryProtection, flags, fileDescriptor, offsetRetyped);
|
||||
|
||||
if (*result != MAP_FAILED) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -123,7 +116,7 @@ int TRI_MMFile(void* memoryAddress,
|
|||
}
|
||||
|
||||
|
||||
int TRI_UNMMFile(void* memoryAddress, size_t numOfBytesToUnMap, void* fileHandle, void** mmHandle) {
|
||||
int TRI_UNMMFile(void* memoryAddress, size_t numOfBytesToUnMap, int fileDescriptor, void** mmHandle) {
|
||||
int result;
|
||||
|
||||
assert(*mmHandle == NULL);
|
||||
|
@ -142,7 +135,7 @@ int TRI_UNMMFile(void* memoryAddress, size_t numOfBytesToUnMap, void* fileHandle
|
|||
}
|
||||
|
||||
|
||||
int TRI_ProtectMMFile(void* memoryAddress, size_t numOfBytesToProtect, int flags, void* fileHandle, void** mmHandle) {
|
||||
int TRI_ProtectMMFile(void* memoryAddress, size_t numOfBytesToProtect, int flags, int fileDescriptor, void** mmHandle) {
|
||||
int result;
|
||||
|
||||
assert(*mmHandle == NULL);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_FlushMMFile(void* fileHandle, void** mmHandle, void* startingAddress, size_t numOfBytesToFlush, int flags) {
|
||||
int TRI_FlushMMFile(int fileDescriptor, void** mmHandle, void* startingAddress, size_t numOfBytesToFlush, int flags) {
|
||||
|
||||
// ...........................................................................
|
||||
// Possible flags to send are (based upon the Ubuntu Linux ASM include files:
|
||||
|
@ -51,11 +51,44 @@ int TRI_FlushMMFile(void* fileHandle, void** mmHandle, void* startingAddress, si
|
|||
// FlushFileBuffers ensures file written to disk
|
||||
// ...........................................................................
|
||||
|
||||
bool ok = FlushViewOfFile(startingAddress, numOfBytesToFlush);
|
||||
if (ok && ((flags & MS_SYNC) == MS_SYNC)) {
|
||||
ok = FlushFileBuffers(fileHandle);
|
||||
|
||||
// ...........................................................................
|
||||
// Whenever we talk to the memory map functions, we require a file handle
|
||||
// rather than a file descriptor. However, we only store file descriptors for
|
||||
// now - this may change.
|
||||
// ...........................................................................
|
||||
|
||||
HANDLE fileHandle;
|
||||
BOOL result;
|
||||
|
||||
if (fileDescriptor < 0) { // an invalid file descriptor of course means an invalid handle
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
if (ok) {
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Attempt to convert file descriptor into an operating system file handle
|
||||
// ...........................................................................
|
||||
|
||||
//printf("oreste:_get_osfhandle _get_osfhandle 1000:BEFORE:#############################:file=%d\n",fileDescriptor);
|
||||
fileHandle = (HANDLE)_get_osfhandle(fileDescriptor);
|
||||
//printf("oreste:_get_osfhandle _get_osfhandle 1000:AFTER:#############################:file=%d\n",fileDescriptor);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// An invalid file system handle was returned.
|
||||
// ...........................................................................
|
||||
|
||||
if (fileHandle == INVALID_HANDLE_VALUE ) {
|
||||
return TRI_ERROR_SYS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
result = FlushViewOfFile(startingAddress, numOfBytesToFlush);
|
||||
if (result && ((flags & MS_SYNC) == MS_SYNC)) {
|
||||
result = FlushFileBuffers(fileHandle);
|
||||
}
|
||||
if (result) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
return TRI_ERROR_SYS_ERROR;
|
||||
|
@ -63,13 +96,13 @@ int TRI_FlushMMFile(void* fileHandle, void** mmHandle, void* startingAddress, si
|
|||
|
||||
|
||||
int TRI_MMFile(void* memoryAddress, size_t numOfBytesToInitialise, int memoryProtection,
|
||||
int flags, void* fileHandle, void** mmHandle, int64_t offset, void** result) {
|
||||
int flags, int fileDescriptor, void** mmHandle, int64_t offset, void** result) {
|
||||
|
||||
DWORD objectProtection = PAGE_READONLY;
|
||||
DWORD viewProtection = FILE_MAP_READ;
|
||||
LARGE_INTEGER mmLength;
|
||||
HANDLE fileHandle;
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Set the high and low order 32 bits for using a 64 bit integer
|
||||
// ...........................................................................
|
||||
|
@ -77,6 +110,35 @@ int TRI_MMFile(void* memoryAddress, size_t numOfBytesToInitialise, int memoryPro
|
|||
mmLength.QuadPart = numOfBytesToInitialise;
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// Whenever we talk to the memory map functions, we require a file handle
|
||||
// rather than a file descriptor. However, we only store file descriptors for
|
||||
// now - this may change.
|
||||
// ...........................................................................
|
||||
|
||||
if (fileDescriptor < 0) { // an invalid file descriptor of course means an invalid handle
|
||||
fileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
else {
|
||||
// ...........................................................................
|
||||
// Attempt to convert file descriptor into an operating system file handle
|
||||
// ...........................................................................
|
||||
|
||||
fileHandle = (HANDLE)_get_osfhandle(fileDescriptor);
|
||||
|
||||
// ...........................................................................
|
||||
// An invalid file system handle was returned.
|
||||
// ...........................................................................
|
||||
|
||||
if (fileHandle == INVALID_HANDLE_VALUE ) {
|
||||
LOG_DEBUG("File descriptor converted to an invalid handle");
|
||||
LOG_TRACE("File descriptor converted to an invalid handle");
|
||||
return TRI_ERROR_SYS_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// There are two steps for mapping a file:
|
||||
// Create the handle and then bring the memory mapped file into 'view'
|
||||
|
@ -87,9 +149,14 @@ int TRI_MMFile(void* memoryAddress, size_t numOfBytesToInitialise, int memoryPro
|
|||
// so we assume no execution and only read access
|
||||
// ...........................................................................
|
||||
|
||||
if (fileHandle == NULL) {
|
||||
fileHandle = INVALID_HANDLE_VALUE; // lives in virtual memory rather than a real file
|
||||
}
|
||||
//res = TRI_MMFile(0, maximalSize, PROT_WRITE | PROT_READ, MAP_SHARED, &fd, &mmHandle, 0, &data);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// If the fileHandle (or file descriptor) is set to NULL, then the are not
|
||||
// memory mapping a real file, rather the file resides in virtual memory
|
||||
// ...........................................................................
|
||||
|
||||
|
||||
if ((flags & PROT_READ) == PROT_READ) {
|
||||
|
||||
|
@ -131,27 +198,54 @@ int TRI_MMFile(void* memoryAddress, size_t numOfBytesToInitialise, int memoryPro
|
|||
objectProtection = PAGE_READWRITE;
|
||||
viewProtection = FILE_MAP_ALL_ACCESS;
|
||||
}
|
||||
|
||||
*mmHandle = CreateFileMapping(fileHandle, NULL, objectProtection, mmLength.HighPart, mmLength.LowPart, NULL);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// TODO: determine the correct memory protection and then uncomment
|
||||
// ...........................................................................
|
||||
// *mmHandle = CreateFileMapping(fileHandle, NULL, objectProtection, mmLength.HighPart, mmLength.LowPart, NULL);
|
||||
|
||||
*mmHandle = CreateFileMapping(fileHandle, NULL, PAGE_READWRITE, mmLength.HighPart, mmLength.LowPart, NULL);
|
||||
|
||||
|
||||
// ...........................................................................
|
||||
// If we have failed for some reason return system error for now.
|
||||
// TODO: map windows error codes to triagens.
|
||||
// We do however output some trace information with the errorcode
|
||||
// ...........................................................................
|
||||
if (*mmHandle == NULL) {
|
||||
// we have failure for some reason
|
||||
// TODO: map the error codes of windows to the TRI_ERROR (see function DWORD WINAPI GetLastError(void) );
|
||||
DWORD errorCode = GetLastError();
|
||||
LOG_DEBUG("File descriptor converted to an invalid handle",errorCode);
|
||||
LOG_TRACE("File descriptor converted to an invalid handle",errorCode);
|
||||
return TRI_ERROR_SYS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// ........................................................................
|
||||
// We have a valid handle, now map the view. We let the OS handle where the
|
||||
// view is placed in memory.
|
||||
// We have a valid mm handle, now map the view. We let the OS decide
|
||||
// where this view is placed in memory.
|
||||
// ........................................................................
|
||||
|
||||
*result = MapViewOfFile(*mmHandle, viewProtection, 0, 0, 0);
|
||||
//TODO: fix the viewProtection above *result = MapViewOfFile(*mmHandle, viewProtection, 0, 0, 0);
|
||||
*result = MapViewOfFile(*mmHandle, FILE_MAP_ALL_ACCESS, 0, 0, numOfBytesToInitialise);
|
||||
|
||||
|
||||
// ........................................................................
|
||||
// The map view of file has failed.
|
||||
// ........................................................................
|
||||
|
||||
if (*result == NULL) {
|
||||
DWORD errorCode = GetLastError();
|
||||
CloseHandle(*mmHandle);
|
||||
// we have failure for some reason
|
||||
// TODO: map the error codes of windows to the TRI_ERROR (see function DWORD WINAPI GetLastError(void) );
|
||||
if (errorCode == ERROR_NOT_ENOUGH_MEMORY) {
|
||||
LOG_DEBUG("MapViewOfFile failed with out of memory error %d",errorCode);
|
||||
LOG_TRACE("MapViewOfFile failed with out of memory error %d",errorCode);
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
LOG_DEBUG("MapViewOfFile failed with error code = %d",errorCode);
|
||||
LOG_TRACE("MapViewOfFile failed with error code = %d",errorCode);
|
||||
return TRI_ERROR_SYS_ERROR;
|
||||
}
|
||||
|
||||
|
@ -159,7 +253,7 @@ int TRI_MMFile(void* memoryAddress, size_t numOfBytesToInitialise, int memoryPro
|
|||
}
|
||||
|
||||
|
||||
int TRI_UNMMFile(void* memoryAddress, size_t numOfBytesToUnMap, void* fileHandle, void** mmHandle) {
|
||||
int TRI_UNMMFile(void* memoryAddress, size_t numOfBytesToUnMap, int fileDescriptor, void** mmHandle) {
|
||||
bool ok = UnmapViewOfFile(memoryAddress);
|
||||
ok = (CloseHandle(*mmHandle) && ok);
|
||||
if (!ok) {
|
||||
|
@ -169,7 +263,7 @@ int TRI_UNMMFile(void* memoryAddress, size_t numOfBytesToUnMap, void* fileHandl
|
|||
}
|
||||
|
||||
|
||||
int TRI_ProtectMMFile(void* memoryAddress, size_t numOfBytesToProtect, int flags, void* fileHandle, void** mmHandle) {
|
||||
int TRI_ProtectMMFile(void* memoryAddress, size_t numOfBytesToProtect, int flags, int fileDescriptor, void** mmHandle) {
|
||||
DWORD objectProtection = PAGE_READONLY;
|
||||
DWORD viewProtection = FILE_MAP_READ;
|
||||
LARGE_INTEGER mmLength;
|
||||
|
|
|
@ -39,11 +39,11 @@
|
|||
// Flags used when we create a memory map -- dummy flags for windows for now
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define MAP_SHARED 0x01 /* Share changes */
|
||||
#define MAP_PRIVATE 0x02 /* Changes are private */
|
||||
#define MAP_TYPE 0x0f /* Mask for type of mapping */
|
||||
#define MAP_FIXED 0x10 /* Interpret addr exactly */
|
||||
#define MAP_ANONYMOUS 0x20 /* don't use a file */
|
||||
#define MAP_SHARED 0x01 /* Share changes */
|
||||
#define MAP_PRIVATE 0x02 /* Changes are private */
|
||||
#define MAP_TYPE 0x0f /* Mask for type of mapping */
|
||||
#define MAP_FIXED 0x10 /* Interpret addr exactly */
|
||||
#define MAP_ANONYMOUS 0x20 /* don't use a file */
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -67,7 +67,7 @@ extern "C" {
|
|||
/// @brief flushes changes made in memory back to disk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_FlushMMFile (void* fileHandle,
|
||||
int TRI_FlushMMFile (int fileDescriptor,
|
||||
void** mmHandle,
|
||||
void* startingAddress,
|
||||
size_t numOfBytesToFlush,
|
||||
|
@ -82,7 +82,7 @@ int TRI_MMFile (void* memoryAddress,
|
|||
size_t numOfBytesToInitialise,
|
||||
int memoryProtection,
|
||||
int flags,
|
||||
void* fileHandle,
|
||||
int fileDescriptor,
|
||||
void** mmHandle,
|
||||
int64_t offset,
|
||||
void** result);
|
||||
|
@ -94,7 +94,7 @@ int TRI_MMFile (void* memoryAddress,
|
|||
|
||||
int TRI_UNMMFile (void* memoryAddress,
|
||||
size_t numOfBytesToUnMap,
|
||||
void* fileHandle,
|
||||
int fileDescriptor,
|
||||
void** mmHandle);
|
||||
|
||||
|
||||
|
@ -105,7 +105,7 @@ int TRI_UNMMFile (void* memoryAddress,
|
|||
int TRI_ProtectMMFile (void* memoryAddress,
|
||||
size_t numOfBytesToProtect,
|
||||
int flags,
|
||||
void* fileHandle,
|
||||
int fileDescriptor,
|
||||
void** mmHandle);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- apple
|
||||
// --SECTION-- global
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -51,12 +51,18 @@
|
|||
#define __STDC_LIMIT_MACROS
|
||||
#endif
|
||||
|
||||
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
||||
#undef TRI_PADDING_32
|
||||
#else
|
||||
#define TRI_PADDING_32 1
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- apple
|
||||
// --Section-- apple
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -133,21 +139,29 @@
|
|||
|
||||
#define TRI_CHDIR chdir
|
||||
#define TRI_CLOSE close
|
||||
#define TRI_CLOSE_SOCKET close
|
||||
#define TRI_CREATE(a,b,c) open((a), (b), (c))
|
||||
#define TRI_GETCWD getcwd
|
||||
#define TRI_LSEEK lseek
|
||||
#define TRI_MKDIR(a,b) mkdir((a), (b))
|
||||
#define TRI_OPEN(a,b) open((a), (b))
|
||||
#define TRI_READ read
|
||||
#define TRI_READ_SOCKET(a,b,c,d) read((a), (b), (c))
|
||||
#define TRI_RMDIR rmdir
|
||||
#define TRI_SLEEP sleep
|
||||
#define TRI_UNLINK unlink
|
||||
#define TRI_WRITE write
|
||||
#define TRI_WRITE_SOCKET(a,b,c,d) write((a), (b), (c))
|
||||
|
||||
#define TRI_LAST_ERROR_STR strerror(errno)
|
||||
|
||||
#define TRI_uid_t uid_t
|
||||
#define TRI_gid_t gid_t
|
||||
|
||||
typedef int socket_t;
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -211,21 +225,29 @@
|
|||
|
||||
#define TRI_CHDIR chdir
|
||||
#define TRI_CLOSE close
|
||||
#define TRI_CLOSE_SOCKET close
|
||||
#define TRI_CREATE(a,b,c) open((a), (b), (c))
|
||||
#define TRI_LSEEK lseek
|
||||
#define TRI_GETCWD getcwd
|
||||
#define TRI_MKDIR(a,b) mkdir((a), (b))
|
||||
#define TRI_OPEN(a,b) open((a), (b))
|
||||
#define TRI_READ read
|
||||
#define TRI_READ_SOCKET(a,b,c,d) read((a), (b), (c))
|
||||
#define TRI_RMDIR rmdir
|
||||
#define TRI_SLEEP sleep
|
||||
#define TRI_UNLINK unlink
|
||||
#define TRI_WRITE write
|
||||
#define TRI_WRITE_SOCKET(a,b,c,d) write((a), (b), (c))
|
||||
|
||||
#define TRI_LAST_ERROR_STR strerror(errno)
|
||||
|
||||
#define TRI_uid_t uid_t
|
||||
#define TRI_gid_t gid_t
|
||||
|
||||
typedef int socket_t;
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -323,21 +345,29 @@
|
|||
|
||||
#define TRI_CHDIR chdir
|
||||
#define TRI_CLOSE close
|
||||
#define TRI_CLOSE_SOCKET close
|
||||
#define TRI_CREATE(a,b,c) open((a), (b), (c))
|
||||
#define TRI_LSEEK lseek
|
||||
#define TRI_GETCWD getcwd
|
||||
#define TRI_MKDIR(a,b) mkdir((a), (b))
|
||||
#define TRI_OPEN(a,b) open((a), (b))
|
||||
#define TRI_READ read
|
||||
#define TRI_READ_SOCKET(a,b,c,d) read((a), (b), (c))
|
||||
#define TRI_RMDIR rmdir
|
||||
#define TRI_SLEEP sleep
|
||||
#define TRI_UNLINK unlink
|
||||
#define TRI_WRITE write
|
||||
#define TRI_WRITE_SOCKET(a,b,c,d) write((a), (b), (c))
|
||||
|
||||
#define TRI_LAST_ERROR_STR strerror(errno)
|
||||
|
||||
#define TRI_uid_t uid_t
|
||||
#define TRI_gid_t gid_t
|
||||
|
||||
typedef int socket_t;
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -358,11 +388,22 @@
|
|||
#define TRI_DIR_SEPARATOR_CHAR '\\'
|
||||
#define TRI_DIR_SEPARATOR_STR "\\"
|
||||
|
||||
// ..............................................................................
|
||||
// This directive below suppresses warnings about using the 'new' more secure CRT
|
||||
// functions.
|
||||
// ..............................................................................
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
|
||||
|
||||
// ..............................................................................
|
||||
// This directive below provides a manner in which the 'new' more secure functions
|
||||
// for example, strcpy is automatically converted to strcpy_s. This is enabled
|
||||
// by default. We have disabled it here.
|
||||
// ..............................................................................
|
||||
//#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <WinSock2.h>
|
||||
|
||||
#define TRI_WIN32_CONSOLE 1
|
||||
#define TRI_WIN32_THREAD_LOCAL_STORAGE 1
|
||||
|
@ -423,27 +464,46 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __BOOL_DEFINED
|
||||
typedef unsigned int bool;
|
||||
//typedef unsigned int bool; - this never ever going to work. Problem is sizeof(bool) in VS C++ is 1 byte and
|
||||
// sizeof(bool) in VS C (C compiler) is -- whatever you want. However, when structures are interchanged between
|
||||
// C & C++ (as in arango) all hell will break loose.
|
||||
typedef unsigned char bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
|
||||
|
||||
// we do not have owner read and owner write under windows
|
||||
// so map these to global read, global write
|
||||
// these are used when creating a file
|
||||
#define S_IRUSR _S_IREAD
|
||||
#define S_IWUSR _S_IWRITE
|
||||
#define S_IRGRP _S_IREAD
|
||||
#define S_IWGRP _S_IWRITE
|
||||
|
||||
#define O_RDONLY _O_RDONLY
|
||||
#define TRI_CHDIR _chdir
|
||||
#define TRI_CLOSE _close
|
||||
#define TRI_CREATE(a,b,c) _open((a), (b))
|
||||
#define TRI_CLOSE_SOCKET TRI_WIN_closesocket
|
||||
/* #define TRI_CREATE(a,b,c) _open((a), (b), (c)) */
|
||||
#define TRI_CREATE(a,b,c) TRI_createFile((a), (b), (c))
|
||||
#define TRI_GETCWD _getcwd
|
||||
#define TRI_LSEEK _lseek
|
||||
#define TRI_MKDIR(a,b) _mkdir((a))
|
||||
#define TRI_OPEN(a,b) _open((a), (b))
|
||||
/* #define TRI_OPEN(a,b) _open((a), (b)) */
|
||||
#define TRI_OPEN(a,b) TRI_openFile((a), (b))
|
||||
#define TRI_READ _read
|
||||
#define TRI_READ_SOCKET(a,b,c,d) recv((a), (b), (c), (d))
|
||||
#define TRI_RMDIR _rmdir
|
||||
#define TRI_SLEEP TRI_sleep
|
||||
#define TRI_UNLINK _unlink
|
||||
#define TRI_WRITE _write
|
||||
#define TRI_WRITE_SOCKET(a,b,c,d) send((a), (b), (c), (d))
|
||||
|
||||
#define TRI_LAST_ERROR_STR strerror(errno)
|
||||
|
||||
|
@ -467,6 +527,7 @@ typedef unsigned int bool;
|
|||
#define alloca _alloca
|
||||
|
||||
|
||||
typedef SOCKET socket_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -483,7 +544,6 @@ typedef unsigned int bool;
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef int socket_t;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -320,6 +320,9 @@ size_t TRI_CharLengthUtf8String (const char*);
|
|||
|
||||
char* TRI_PrefixUtf8String (const char*, const uint32_t);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief threads in win32
|
||||
/// @brief threads in win32 & win64
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
|
@ -205,6 +205,7 @@ void TRI_JoinThread (TRI_thread_t* thread) {
|
|||
|
||||
bool TRI_SignalThread (TRI_thread_t* thread, int signum) {
|
||||
// TODO: NO NATIVE implementation of signals
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -213,7 +214,13 @@ bool TRI_SignalThread (TRI_thread_t* thread, int signum) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TRI_IsSelfThread (TRI_thread_t* thread) {
|
||||
return ( GetCurrentThreadId() == GetThreadId(thread) );
|
||||
return false;
|
||||
// ...........................................................................
|
||||
// The GetThreadID(...) function is only available in Windows Vista or Higher
|
||||
// TODO: Change the TRI_thread_t into a structure which stores the thread id
|
||||
// as well as the thread handle. This can then be passed around
|
||||
// ...........................................................................
|
||||
//return ( GetCurrentThreadId() == GetThreadId(thread) );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,116 +1,359 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief some utilities for windows
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Dr. O
|
||||
/// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include "win-utils.h"
|
||||
|
||||
/*
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <share.h>
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup Windows_Utilties
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief some utilities for windows
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Dr. O
|
||||
/// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <io.h>
|
||||
#include "win-utils.h"
|
||||
#include <BasicsC/logging.h>
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <crtdbg.h>
|
||||
|
||||
/*
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <share.h>
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// .............................................................................
|
||||
// Some global variables which may be required throughtout the lifetime of the
|
||||
// server
|
||||
// .............................................................................
|
||||
|
||||
_invalid_parameter_handler oldInvalidHandleHandler;
|
||||
_invalid_parameter_handler newInvalidHandleHandler;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup Windows_Utilties
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int ftruncate(int fd, long newSize) {
|
||||
int result = _chsize(fd, newSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int getpagesize(void) {
|
||||
static int pageSize = 0; // only define it once
|
||||
|
||||
if (!pageSize) {
|
||||
// first time, so call the system info function
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetSystemInfo (&systemInfo);
|
||||
pageSize = systemInfo.dwPageSize;
|
||||
}
|
||||
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Calls the windows Sleep function which always sleeps for milliseconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_sleep(unsigned long waitTime) {
|
||||
Sleep(waitTime * 1000);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Calls a timer which waits for a signal after the elapsed time.
|
||||
// The timer is acurate to 100nanoseconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_usleep(unsigned long waitTime) {
|
||||
int result;
|
||||
HANDLE hTimer = NULL; // stores the handle of the timer object
|
||||
LARGE_INTEGER wTime; // essentially a 64bit number
|
||||
wTime.QuadPart = waitTime * 10; // *10 to change to microseconds
|
||||
wTime.QuadPart = -wTime.QuadPart; // negative indicates relative time elapsed,
|
||||
|
||||
// Create an unnamed waitable timer.
|
||||
hTimer = CreateWaitableTimer(NULL, 1, NULL);
|
||||
if (hTimer == NULL) {
|
||||
// no much we can do at this low level
|
||||
return;
|
||||
}
|
||||
|
||||
// Set timer to wait for indicated micro seconds.
|
||||
if (!SetWaitableTimer(hTimer, &wTime, 0, NULL, NULL, 0)) {
|
||||
// no much we can do at this low level
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the timer - but don't wait for ever.
|
||||
result = WaitForSingleObject(hTimer, ((waitTime/1000) + 1)); // wait for a 1 millisecond at least
|
||||
|
||||
// todo: go through what the result is e.g. WAIT_OBJECT_0
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
||||
// End:
|
||||
|
||||
|
||||
|
||||
int getpagesize(void) {
|
||||
static int pageSize = 0; // only define it once
|
||||
|
||||
if (!pageSize) {
|
||||
// first time, so call the system info function
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetSystemInfo (&systemInfo);
|
||||
pageSize = systemInfo.dwPageSize;
|
||||
}
|
||||
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Calls the windows Sleep function which always sleeps for milliseconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_sleep(unsigned long waitTime) {
|
||||
Sleep(waitTime * 1000);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Calls a timer which waits for a signal after the elapsed time.
|
||||
// The timer is acurate to 100nanoseconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_usleep(unsigned long waitTime) {
|
||||
int result;
|
||||
HANDLE hTimer = NULL; // stores the handle of the timer object
|
||||
LARGE_INTEGER wTime; // essentially a 64bit number
|
||||
wTime.QuadPart = waitTime * 10; // *10 to change to microseconds
|
||||
wTime.QuadPart = -wTime.QuadPart; // negative indicates relative time elapsed,
|
||||
|
||||
// Create an unnamed waitable timer.
|
||||
hTimer = CreateWaitableTimer(NULL, 1, NULL);
|
||||
if (hTimer == NULL) {
|
||||
// no much we can do at this low level
|
||||
return;
|
||||
}
|
||||
|
||||
// Set timer to wait for indicated micro seconds.
|
||||
if (!SetWaitableTimer(hTimer, &wTime, 0, NULL, NULL, 0)) {
|
||||
// no much we can do at this low level
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the timer - but don't wait for ever.
|
||||
result = WaitForSingleObject(hTimer, ((waitTime/1000) + 1)); // wait for a 1 millisecond at least
|
||||
|
||||
// todo: go through what the result is e.g. WAIT_OBJECT_0
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Sets up a handler when invalid (win) handles are passed to a windows function.
|
||||
// This is not of much use since no values can be returned. All we can do
|
||||
// for now is to ignore error and hope it goes away!
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void InvalidParameterHandler(const wchar_t* expression, // expression sent to function - NULL
|
||||
const wchar_t* function, // name of function - NULL
|
||||
const wchar_t* file, // file where code resides - NULL
|
||||
unsigned int line, // line within file - NULL
|
||||
uintptr_t pReserved) { // in case microsoft forget something
|
||||
LOG_ERROR("Invalid handle parameter passed");
|
||||
|
||||
/* start oreste -debug */
|
||||
if (expression != 0) {
|
||||
wprintf(L"win-utils.c:InvalidParameterHandler:EXPRESSION = %s\n",expression);
|
||||
}
|
||||
else {
|
||||
wprintf(L"win-utils.c:InvalidParameterHandler:EXPRESSION = NULL\n");
|
||||
}
|
||||
if (function != 0) {
|
||||
wprintf(L"win-utils.c:InvalidParameterHandler:FUNCTION = %s\n",function);
|
||||
}
|
||||
else {
|
||||
wprintf(L"win-utils.c:InvalidParameterHandler:FUNCTION = NULL\n");
|
||||
}
|
||||
if (file!= 0) {
|
||||
wprintf(L"win-utils.c:InvalidParameterHandler:FILE = %s\n",file);
|
||||
}
|
||||
else {
|
||||
wprintf(L"win-utils.c:InvalidParameterHandler:FILE = NULL\n");
|
||||
}
|
||||
printf("oreste:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%:win-utils.c:InvalidParameterHandler:LINE = %ud\n",line);
|
||||
/* end oreste -debug */
|
||||
//abort();
|
||||
// TODO: use the wcstombs_s function to convert wchar to char - since all the above
|
||||
// wchar never will contain 2 byte chars
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Called from the 'main' and performs any initialisation requirements which
|
||||
// are specific to windows.
|
||||
//
|
||||
// If this function returns 0, then no errors encountered. If < 0, then the
|
||||
// calling function should terminate the application. If > 0, then the
|
||||
// calling function should decide what to do.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int finaliseWindows(const TRI_win_finalise_e finaliseWhat, const char* data) {
|
||||
|
||||
// ............................................................................
|
||||
// The data is used to transport information from the calling function to here
|
||||
// it may be NULL (and will be in most cases)
|
||||
// ............................................................................
|
||||
|
||||
switch (finaliseWhat) {
|
||||
|
||||
case TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL: {
|
||||
int errorCode;
|
||||
errorCode = WSACleanup();
|
||||
if (errorCode != 0) {
|
||||
// can not use LOG_ etc here since the logging may have terminated
|
||||
printf("ERROR: Could not perform a valid Winsock2 cleanup. WSACleanup returned error %d.",errorCode);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
default: {
|
||||
// can not use LOG_ etc here since the logging may have terminated
|
||||
printf("ERROR: Invalid windows finalisation called");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int initialiseWindows(const TRI_win_initialise_e initialiseWhat, const char* data) {
|
||||
|
||||
|
||||
// ............................................................................
|
||||
// The data is used to transport information from the calling function to here
|
||||
// it may be NULL (and will be in most cases)
|
||||
// ............................................................................
|
||||
|
||||
switch (initialiseWhat) {
|
||||
|
||||
case TRI_WIN_INITIAL_SET_DEBUG_FLAG: {
|
||||
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF)|_CRTDBG_CHECK_ALWAYS_DF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ...........................................................................
|
||||
// Assign a handler for invalid handles
|
||||
// ...........................................................................
|
||||
|
||||
case TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER: {
|
||||
newInvalidHandleHandler = InvalidParameterHandler;
|
||||
oldInvalidHandleHandler = _set_invalid_parameter_handler(newInvalidHandleHandler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL: {
|
||||
int errorCode;
|
||||
WSADATA wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2,2);
|
||||
errorCode = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (errorCode != 0) {
|
||||
LOG_ERROR("Could not find a usuable Winsock DLL. WSAStartup returned an error.");
|
||||
return -1;
|
||||
}
|
||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
||||
LOG_ERROR("Could not find a usuable Winsock DLL. WSAStartup did not return version 2.2.");
|
||||
WSACleanup();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
default: {
|
||||
LOG_ERROR("Invalid windows initialisation called");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
int TRI_WIN_closesocket(SOCKET s) {
|
||||
int res;
|
||||
res = shutdown(s,2);
|
||||
res = closesocket(s);
|
||||
return res;
|
||||
}
|
||||
|
||||
int TRI_createFile (const char* filename, int openFlags, int modeFlags) {
|
||||
HANDLE fileHandle;
|
||||
int fileDescriptor;
|
||||
|
||||
|
||||
fileHandle = CreateFileA(filename,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (fileHandle == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fileDescriptor = _open_osfhandle( (intptr_t)(fileHandle), O_RDWR| _O_BINARY);
|
||||
return fileDescriptor;
|
||||
|
||||
/*
|
||||
#define O_RDONLY _O_RDONLY
|
||||
#define O_WRONLY _O_WRONLY
|
||||
#define O_RDWR _O_RDWR
|
||||
#define O_APPEND _O_APPEND
|
||||
#define O_CREAT _O_CREAT
|
||||
#define O_TRUNC _O_TRUNC
|
||||
#define O_EXCL _O_EXCL
|
||||
#define O_TEXT _O_TEXT
|
||||
#define O_BINARY _O_BINARY
|
||||
#define O_RAW _O_BINARY
|
||||
#define O_TEMPORARY _O_TEMPORARY
|
||||
#define O_NOINHERIT _O_NOINHERIT
|
||||
#define O_SEQUENTIAL _O_SEQUENTIAL
|
||||
#define O_RANDOM _O_RANDOM
|
||||
//filename, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR
|
||||
*/
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Creates or opens a file using the windows CreateFile method. Notice below we
|
||||
// have used the method CreateFileA to avoid unicode characters - for now anyway
|
||||
// TODO oreste: map the flags e.g. O_RDWR to the equivalents for CreateFileA
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_openFile (const char* filename, int openFlags) {
|
||||
HANDLE fileHandle;
|
||||
int fileDescriptor;
|
||||
|
||||
fileHandle = CreateFileA(filename,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL);
|
||||
if (fileHandle == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fileDescriptor = _open_osfhandle( (intptr_t)(fileHandle), O_RDWR| _O_BINARY);
|
||||
return fileDescriptor;
|
||||
|
||||
/*
|
||||
#define O_RDONLY _O_RDONLY
|
||||
#define O_WRONLY _O_WRONLY
|
||||
#define O_RDWR _O_RDWR
|
||||
#define O_APPEND _O_APPEND
|
||||
#define O_CREAT _O_CREAT
|
||||
#define O_TRUNC _O_TRUNC
|
||||
#define O_EXCL _O_EXCL
|
||||
#define O_TEXT _O_TEXT
|
||||
#define O_BINARY _O_BINARY
|
||||
#define O_RAW _O_BINARY
|
||||
#define O_TEMPORARY _O_TEMPORARY
|
||||
#define O_NOINHERIT _O_NOINHERIT
|
||||
#define O_SEQUENTIAL _O_SEQUENTIAL
|
||||
#define O_RANDOM _O_RANDOM
|
||||
//filename, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
||||
// End:
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#ifndef TRIAGENS_BASICS_C_WIN_UTILS_H
|
||||
#define TRIAGENS_BASICS_C_WIN_UTILS_H 1
|
||||
|
||||
#include <WinSock2.h>
|
||||
|
||||
/* Constants rounded for 21 decimals.
|
||||
#define M_E 2.71828182845904523536
|
||||
|
@ -55,6 +56,25 @@ extern "C" {
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// .............................................................................
|
||||
// Called before anything else starts - initialises whatever is required to be
|
||||
// initalised.
|
||||
// .............................................................................
|
||||
typedef enum {
|
||||
TRI_WIN_FINAL_SET_INVALID_HANLE_HANDLER,
|
||||
TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL
|
||||
}
|
||||
TRI_win_finalise_e;
|
||||
|
||||
typedef enum {
|
||||
TRI_WIN_INITIAL_SET_DEBUG_FLAG,
|
||||
TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER,
|
||||
TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL
|
||||
}
|
||||
TRI_win_initialise_e;
|
||||
|
||||
int finaliseWindows (const TRI_win_finalise_e, const char*);
|
||||
int initialiseWindows (const TRI_win_initialise_e, const char*);
|
||||
|
||||
// .............................................................................
|
||||
// windows equivalent of ftruncate (the truncation of an open file) is
|
||||
|
@ -71,6 +91,18 @@ int ftruncate (int, long);
|
|||
int getpagesize (void);
|
||||
|
||||
|
||||
|
||||
int TRI_WIN_closesocket (SOCKET);
|
||||
|
||||
// .............................................................................
|
||||
// This function uses the CreateFile windows method rather than _open which
|
||||
// then will allow the application to rename files on the fly.
|
||||
// .............................................................................
|
||||
|
||||
int TRI_createFile (const char* filename, int openFlags, int modeFlags);
|
||||
|
||||
int TRI_openFile (const char* filename, int openFlags);
|
||||
|
||||
// .............................................................................
|
||||
// the sleep function in windows is for milliseconds, on linux it is for seconds
|
||||
// this provides a translation
|
||||
|
|
|
@ -110,9 +110,17 @@ namespace triagens {
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup (Scheduler* scheduler, EventLoop loop) {
|
||||
SocketTask::setup(scheduler, loop);
|
||||
AsyncTask::setup(scheduler, loop);
|
||||
bool setup (Scheduler* scheduler, EventLoop loop) {
|
||||
bool ok;
|
||||
ok = SocketTask::setup(scheduler, loop);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
ok = AsyncTask::setup(scheduler, loop);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
#ifndef TRIAGENS_GENERAL_SERVER_GENERAL_COMM_TASK_H
|
||||
#define TRIAGENS_GENERAL_SERVER_GENERAL_COMM_TASK_H 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "BasicsC/win-utils.h"
|
||||
#endif
|
||||
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include "Basics/StringBuffer.h"
|
||||
|
@ -100,6 +104,7 @@ namespace triagens {
|
|||
<< ", client ip " << _connectionInfo.clientAddress
|
||||
<< ", client port " << _connectionInfo.clientPort;
|
||||
|
||||
|
||||
pair<size_t, size_t> p = server->getHandlerFactory()->sizeRestrictions();
|
||||
|
||||
_maximalHeaderSize = p.first;
|
||||
|
@ -169,7 +174,8 @@ namespace triagens {
|
|||
|
||||
void beginShutdown () {
|
||||
if (commSocket != -1) {
|
||||
TRI_CLOSE(commSocket);
|
||||
TRI_CLOSE_SOCKET(commSocket);
|
||||
commSocket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -190,6 +190,7 @@ namespace triagens {
|
|||
LOGGER_TRACE << "trying to bind to endpoint '" << (*i)->getSpecification() << "' for requests";
|
||||
|
||||
bool ok = openEndpoint(*i);
|
||||
|
||||
if (ok) {
|
||||
LOGGER_DEBUG << "bound to endpoint '" << (*i)->getSpecification() << "'";
|
||||
}
|
||||
|
@ -418,11 +419,15 @@ namespace triagens {
|
|||
bool openEndpoint (Endpoint* endpoint) {
|
||||
ListenTask* task = new GeneralListenTask<S> (dynamic_cast<S*> (this), endpoint, true);
|
||||
|
||||
if (! task->isBound()) {
|
||||
// ...................................................................
|
||||
// For some reason we have failed in our endeavour to bind to the socket -
|
||||
// this effectively terminates the server
|
||||
// ...................................................................
|
||||
|
||||
if (! task->isBound()) {
|
||||
deleteTask(task);
|
||||
return false;
|
||||
}
|
||||
|
||||
_scheduler->registerTask(task);
|
||||
_listenTasks.push_back(task);
|
||||
|
||||
|
|
|
@ -127,11 +127,13 @@ namespace triagens {
|
|||
|
||||
bool processRead () {
|
||||
if (this->_requestPending || this->_readBuffer->c_str() == 0) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handleRequest = false;
|
||||
|
||||
|
||||
if (! this->_readRequestBody) {
|
||||
#ifdef TRI_ENABLE_FIGURES
|
||||
|
||||
|
@ -151,8 +153,10 @@ namespace triagens {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
size_t headerLength = ptr - this->_readBuffer->c_str();
|
||||
|
||||
|
||||
if (headerLength > this->_maximalHeaderSize) {
|
||||
LOGGER_WARNING << "maximal header size is " << this->_maximalHeaderSize << ", request header size is " << headerLength;
|
||||
// header is too large
|
||||
|
@ -354,6 +358,7 @@ namespace triagens {
|
|||
|
||||
// authenticated
|
||||
if (auth) {
|
||||
|
||||
HttpHandler* handler = this->_server->getHandlerFactory()->createHandler(this->_request);
|
||||
bool ok = false;
|
||||
|
||||
|
|
|
@ -238,11 +238,13 @@ HttpHandler* HttpHandlerFactory::createHandler (HttpRequest* request) {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
map<string, create_fptr> const& ii = _constructors;
|
||||
string path = request->requestPath();
|
||||
map<string, create_fptr>::const_iterator i = ii.find(path);
|
||||
void* data = 0;
|
||||
|
||||
|
||||
// no direct match, check prefix matches
|
||||
if (i == ii.end()) {
|
||||
LOGGER_TRACE << "no direct handler found, trying prefixes";
|
||||
|
@ -327,6 +329,7 @@ HttpHandler* HttpHandlerFactory::createHandler (HttpRequest* request) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// look up data
|
||||
map<string, void*> const& jj = _datas;
|
||||
map<string, void*>::const_iterator j = jj.find(path);
|
||||
|
|
|
@ -169,8 +169,7 @@ static void OutputMachine (string const& text, LoggerData::Info const& info) {
|
|||
switch (info._category) {
|
||||
case TRI_LOG_CATEGORY_FATAL: line.appendText("FATAL"); break;
|
||||
case TRI_LOG_CATEGORY_ERROR: line.appendText("ERROR"); break;
|
||||
case TRI_LOG_CATEGORY_WARNING: line.appendText("WARNING"); break;
|
||||
|
||||
case TRI_LOG_CATEGORY_WARNING: { line.appendText("WARNING"); break; }
|
||||
case TRI_LOG_CATEGORY_REQUEST_IN_START: line.appendText("REQUEST-IN-START"); break;
|
||||
case TRI_LOG_CATEGORY_REQUEST_IN_END: line.appendText("REQUEST-IN-END"); break;
|
||||
case TRI_LOG_CATEGORY_REQUEST_OUT_START: line.appendText("REQUEST-OUT-START"); break;
|
||||
|
|
|
@ -204,6 +204,27 @@ Info::Info ()
|
|||
_prefix() {
|
||||
}
|
||||
|
||||
Info::Info (const Info& originalInfo) {
|
||||
_messageIdentifier._name = originalInfo._messageIdentifier._name;
|
||||
_level = originalInfo._level;
|
||||
_category = originalInfo._category;
|
||||
_severity = originalInfo._severity;
|
||||
_functional._name = originalInfo._functional._name;
|
||||
_peg._name = originalInfo._peg._name;
|
||||
_task._name = originalInfo._task._name;
|
||||
_position._file = originalInfo._position._file;
|
||||
_position._function = originalInfo._position._function;
|
||||
_position._line = originalInfo._position._line;
|
||||
_measure._unit = originalInfo._measure._unit;
|
||||
_measure._value = originalInfo._measure._value;
|
||||
_extras.clear();
|
||||
for (vector<Extra>::const_iterator i = originalInfo._extras.begin(); i != originalInfo._extras.end(); ++i) {
|
||||
_extras.push_back(Extra((*i)._position, (*i)._name));
|
||||
}
|
||||
_userIdentifier._user = originalInfo._userIdentifier._user;
|
||||
_prefix = originalInfo._prefix;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -226,6 +226,8 @@ namespace triagens {
|
|||
struct Info {
|
||||
Info();
|
||||
|
||||
Info(const Info& originalInfo);
|
||||
|
||||
static ApplicationName _applicationName;
|
||||
static Facility _facility;
|
||||
static HostName _hostName;
|
||||
|
|
|
@ -124,8 +124,7 @@ static void computeInfo (LoggerData::Info& info) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LoggerStream::LoggerStream () :
|
||||
_stream(new stringstream()),
|
||||
_info() {
|
||||
_stream(new stringstream()), _info() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -141,8 +140,7 @@ LoggerStream::LoggerStream (LoggerData::Info const& info) :
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LoggerStream::LoggerStream (LoggerStream const& copy) :
|
||||
_stream(new stringstream()), _info(copy._info) {
|
||||
_stream->str(copy._stream->str());
|
||||
_stream(copy._stream), _info(copy._info) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -154,7 +152,6 @@ LoggerStream::~LoggerStream () {
|
|||
|
||||
if (_stream != 0) {
|
||||
Logger::output(static_cast<stringstream*> (_stream)->str(), _info);
|
||||
|
||||
delete _stream;
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +187,6 @@ LoggerStream& LoggerStream::operator<< (std::ostream& (*fptr) (std::ostream&)) {
|
|||
|
||||
LoggerStream& LoggerStream::operator<< (TRI_log_level_e level) {
|
||||
_info._level = level;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -200,7 +196,6 @@ LoggerStream& LoggerStream::operator<< (TRI_log_level_e level) {
|
|||
|
||||
LoggerStream& LoggerStream::operator<< (TRI_log_category_e category) {
|
||||
_info._category = category;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -210,7 +205,6 @@ LoggerStream& LoggerStream::operator<< (TRI_log_category_e category) {
|
|||
|
||||
LoggerStream& LoggerStream::operator<< (TRI_log_severity_e severity) {
|
||||
_info._severity = severity;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -306,6 +300,7 @@ LoggerStream& LoggerStream::operator<< (LoggerData::UserIdentifier const& userId
|
|||
return *this;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -236,6 +236,7 @@ namespace triagens {
|
|||
|
||||
stringstream* _stream;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief information
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -212,7 +212,7 @@ static void CreateDoubleOption (po_double_t * desc, void const * input, void * o
|
|||
|
||||
po = (TRI_program_options_t*) (output);
|
||||
|
||||
InitOptionStructure(&doubleOpt, desc->base._name, 1, 0, po->_longopts._length);
|
||||
InitOptionStructure(&doubleOpt, desc->base._name, 1, 0, (int)(po->_longopts._length));
|
||||
|
||||
memset(&item, 0, sizeof(item));
|
||||
|
||||
|
|
|
@ -401,7 +401,6 @@ int AnyServer::start () {
|
|||
WritePidFile(_pidFile, TRI_CurrentProcessId());
|
||||
}
|
||||
|
||||
prepareServer();
|
||||
int res = startupServer();
|
||||
|
||||
if (! _pidFile.empty()) {
|
||||
|
|
|
@ -197,7 +197,7 @@ namespace triagens {
|
|||
/// @brief connect the endpoint
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual int connect (double, double) = 0;
|
||||
virtual socket_t connect (double, double) = 0;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief disconnect the endpoint
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Dr. O
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -117,19 +118,18 @@ EndpointIp::~EndpointIp () {
|
|||
socket_t EndpointIp::connectSocket (const struct addrinfo* aip, double connectTimeout, double requestTimeout) {
|
||||
// set address and port
|
||||
char host[NI_MAXHOST], serv[NI_MAXSERV];
|
||||
if (getnameinfo(aip->ai_addr, aip->ai_addrlen,
|
||||
if (::getnameinfo(aip->ai_addr, aip->ai_addrlen,
|
||||
host, sizeof(host),
|
||||
serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||
|
||||
LOGGER_TRACE << "bind to address '" << string(host) << "' port '" << _port << "'";
|
||||
}
|
||||
|
||||
int listenSocket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
|
||||
if (listenSocket == -1) {
|
||||
socket_t listenSocket = ::socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
|
||||
if (listenSocket == INVALID_SOCKET) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (_type == ENDPOINT_SERVER) {
|
||||
// try to reuse address
|
||||
int opt = 1;
|
||||
|
@ -143,21 +143,21 @@ socket_t EndpointIp::connectSocket (const struct addrinfo* aip, double connectTi
|
|||
LOGGER_TRACE << "reuse address flag set";
|
||||
|
||||
// server needs to bind to socket
|
||||
int result = bind(listenSocket, aip->ai_addr, aip->ai_addrlen);
|
||||
int result = ::bind(listenSocket, aip->ai_addr, aip->ai_addrlen);
|
||||
if (result != 0) {
|
||||
// error
|
||||
TRI_CLOSE(listenSocket);
|
||||
|
||||
TRI_CLOSE_SOCKET(listenSocket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// listen for new connection, executed for server endpoints only
|
||||
LOGGER_TRACE << "using backlog size " << _listenBacklog;
|
||||
result = listen(listenSocket, _listenBacklog);
|
||||
|
||||
if (result < 0) {
|
||||
TRI_CLOSE(listenSocket);
|
||||
result = ::listen(listenSocket, _listenBacklog);
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
TRI_CLOSE_SOCKET(listenSocket);
|
||||
// todo: get the correct error code using WSAGetLastError for windows
|
||||
LOGGER_ERROR << "listen failed with " << errno << " (" << strerror(errno) << ")";
|
||||
|
||||
return 0;
|
||||
|
@ -169,15 +169,16 @@ socket_t EndpointIp::connectSocket (const struct addrinfo* aip, double connectTi
|
|||
// set timeout
|
||||
setTimeout(listenSocket, connectTimeout);
|
||||
|
||||
if (::connect(listenSocket, (const struct sockaddr*) aip->ai_addr, aip->ai_addrlen) != 0) {
|
||||
TRI_CLOSE(listenSocket);
|
||||
int result = ::connect(listenSocket, (const struct sockaddr*) aip->ai_addr, aip->ai_addrlen);
|
||||
if ( result == SOCKET_ERROR) {
|
||||
TRI_CLOSE_SOCKET(listenSocket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!setSocketFlags(listenSocket)) {
|
||||
TRI_CLOSE(listenSocket);
|
||||
if (!setSocketFlags(listenSocket)) { // set some common socket flags for a server
|
||||
TRI_CLOSE_SOCKET(listenSocket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -239,7 +240,7 @@ socket_t EndpointIp::connect (double connectTimeout, double requestTimeout) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int listenSocket = 0;
|
||||
socket_t listenSocket = 0;
|
||||
|
||||
// Try all returned addresses until one works
|
||||
for (aip = result; aip != NULL; aip = aip->ai_next) {
|
||||
|
@ -265,7 +266,7 @@ void EndpointIp::disconnect () {
|
|||
assert(_socket);
|
||||
|
||||
_connected = false;
|
||||
TRI_CLOSE(_socket);
|
||||
TRI_CLOSE_SOCKET(_socket);
|
||||
|
||||
_socket = 0;
|
||||
}
|
||||
|
@ -281,6 +282,7 @@ bool EndpointIp::initIncoming (socket_t incoming) {
|
|||
int res = setsockopt(incoming, IPPROTO_TCP, TCP_NODELAY, (char*) &n, sizeof(n));
|
||||
|
||||
if (res != 0 ) {
|
||||
// todo: get correct windows error code
|
||||
LOGGER_WARNING << "setsockopt failed with " << errno << " (" << strerror(errno) << ")";
|
||||
|
||||
return false;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Dr. O
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -125,8 +125,10 @@ namespace triagens {
|
|||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
#ifdef TRI_OPENSSL_VERSION
|
||||
revision = "$Revision: OPENSSL " TRI_OPENSSL_VERSION " $";
|
||||
LOGGER_TRACE << revision;
|
||||
#endif
|
||||
|
||||
#ifdef TRI_LIBEV_VERSION
|
||||
revision = "$Revision: LIBEV " TRI_LIBEV_VERSION " $";
|
||||
|
|
|
@ -193,37 +193,83 @@ static SignalTask* localSignalTask;
|
|||
|
||||
bool CtrlHandler(DWORD eventType) {
|
||||
ControlCTask* ccTask = (ControlCTask*)(localSignalTask);
|
||||
string msg = ccTask->_server->getName() + " [shutting down]";
|
||||
bool shutdown = false;
|
||||
string shutdownMessage;
|
||||
// .........................................................................
|
||||
// TODO: popup message
|
||||
// .........................................................................
|
||||
|
||||
|
||||
switch (eventType) {
|
||||
case CTRL_C_EVENT:
|
||||
case CTRL_CLOSE_EVENT:
|
||||
case CTRL_LOGOFF_EVENT:
|
||||
|
||||
case CTRL_BREAK_EVENT: {
|
||||
//TODO: windows does not appreciate changing the environment in this manner
|
||||
//TRI_SetProcessTitle(msg.c_str());
|
||||
shutdown = true;
|
||||
shutdownMessage = "control-break received";
|
||||
break;
|
||||
}
|
||||
|
||||
case CTRL_C_EVENT: {
|
||||
//TODO: windows does not appreciate changing the environment in this manner
|
||||
//TRI_SetProcessTitle(msg.c_str());
|
||||
shutdown = true;
|
||||
shutdownMessage = "control-c received";
|
||||
break;
|
||||
}
|
||||
|
||||
case CTRL_CLOSE_EVENT: {
|
||||
//TODO: windows does not appreciate changing the environment in this manner
|
||||
//TRI_SetProcessTitle(msg.c_str());
|
||||
shutdown = true;
|
||||
shutdownMessage = "window-close received";
|
||||
break;
|
||||
}
|
||||
|
||||
case CTRL_LOGOFF_EVENT: {
|
||||
//TODO: windows does not appreciate changing the environment in this manner
|
||||
//TRI_SetProcessTitle(msg.c_str());
|
||||
shutdown = true;
|
||||
shutdownMessage = "user-logoff received";
|
||||
break;
|
||||
}
|
||||
|
||||
case CTRL_SHUTDOWN_EVENT: {
|
||||
string msg = ccTask->_server->getName() + " [shutting down]";
|
||||
|
||||
TRI_SetProcessTitle(msg.c_str());
|
||||
|
||||
if (ccTask->_seen == 0) {
|
||||
LOGGER_INFO << "control-c received, beginning shut down sequence";
|
||||
ccTask->_server->beginShutdown();
|
||||
}
|
||||
else {
|
||||
LOGGER_INFO << "control-c received, terminating";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
++ccTask->_seen;
|
||||
|
||||
return true;
|
||||
//TODO: windows does not appreciate changing the environment in this manner
|
||||
//TRI_SetProcessTitle(msg.c_str());
|
||||
shutdown = true;
|
||||
shutdownMessage = "system-shutdown received";
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
shutdown = false;
|
||||
break;
|
||||
}
|
||||
|
||||
} // end of switch statement
|
||||
|
||||
if (shutdown == false) {
|
||||
LOGGER_ERROR << "Invalid CTRL HANDLER event received - ignoring event";
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
if (ccTask->_seen == 0) {
|
||||
LOGGER_INFO << shutdownMessage << ", beginning shut down sequence";
|
||||
ccTask->_server->beginShutdown();
|
||||
++ccTask->_seen;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ............................................................................
|
||||
// user is desperate to kill the server!
|
||||
// ............................................................................
|
||||
|
||||
LOGGER_INFO << shutdownMessage << ", terminating";
|
||||
_exit(EXIT_FAILURE); // quick exit for windows
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -357,7 +403,16 @@ void ApplicationScheduler::setupOptions (map<string, ProgramOptionsDescription>&
|
|||
// .............................................................................
|
||||
|
||||
options[ApplicationServer::OPTIONS_SERVER + ":help-extended"]
|
||||
#ifdef _WIN32
|
||||
// ...........................................................................
|
||||
// since we are trying to use libev, then only select is available
|
||||
// no point in allowing this to be configured at this stage. Perhaps if we
|
||||
// eventually write a native libev we can offer something.
|
||||
// ...........................................................................
|
||||
//("scheduler.backend", &_backend, "1: select, 2: poll, 4: epoll")
|
||||
#else
|
||||
("scheduler.backend", &_backend, "1: select, 2: poll, 4: epoll")
|
||||
#endif
|
||||
("scheduler.report-interval", &_reportIntervall, "scheduler report interval")
|
||||
("server.no-reuse-address", "do not try to reuse address")
|
||||
#ifdef TRI_HAVE_GETRLIMIT
|
||||
|
|
|
@ -58,11 +58,15 @@ void AsyncTask::signal () {
|
|||
// Task methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void AsyncTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
bool AsyncTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
this->scheduler = scheduler;
|
||||
this->loop = loop;
|
||||
|
||||
watcher = scheduler->installAsyncEvent(loop, this);
|
||||
if (watcher == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace triagens {
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup (Scheduler*, EventLoop);
|
||||
bool setup (Scheduler*, EventLoop);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
|
||||
#ifdef TRI_HAVE_WINSOCK2_H
|
||||
#include "BasicsC/win-utils.h"
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#endif
|
||||
|
@ -93,15 +94,20 @@ bool ListenTask::isBound () const {
|
|||
// Task methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ListenTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
bool ListenTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
if (! isBound()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
this->scheduler = scheduler;
|
||||
this->loop = loop;
|
||||
|
||||
readWatcher = scheduler->installSocketEvent(loop, EVENT_SOCKET_READ, this, listenSocket);
|
||||
|
||||
if (readWatcher == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -126,7 +132,6 @@ bool ListenTask::handleEvent (EventToken token, EventType revents) {
|
|||
|
||||
// accept connection
|
||||
socket_t connfd = accept(listenSocket, (sockaddr*) &addr, &len);
|
||||
|
||||
if (connfd == INVALID_SOCKET) {
|
||||
++acceptFailures;
|
||||
|
||||
|
@ -150,7 +155,7 @@ bool ListenTask::handleEvent (EventToken token, EventType revents) {
|
|||
int res = getsockname(connfd, (sockaddr*) &addr_out, &len_out);
|
||||
|
||||
if (res != 0) {
|
||||
TRI_CLOSE(connfd);
|
||||
TRI_CLOSE_SOCKET(connfd);
|
||||
|
||||
LOGGER_WARNING << "getsockname failed with " << errno << " (" << strerror(errno) << ")";
|
||||
|
||||
|
@ -162,7 +167,7 @@ bool ListenTask::handleEvent (EventToken token, EventType revents) {
|
|||
// disable nagle's algorithm, set to non-blocking and close-on-exec
|
||||
bool result = _endpoint->initIncoming(connfd);
|
||||
if (!result) {
|
||||
TRI_CLOSE(connfd);
|
||||
TRI_CLOSE_SOCKET(connfd);
|
||||
|
||||
// TODO GeneralFigures::incCounter<GeneralFigures::GeneralServerStatistics::connectErrorsAccessor>();
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace triagens {
|
|||
/// Note that registerTask must only be called when the socket is bound.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup (Scheduler*, EventLoop);
|
||||
bool setup (Scheduler*, EventLoop);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
|
|
|
@ -63,11 +63,15 @@ void PeriodicTask::resetTimer (double offset, double intervall) {
|
|||
// Task methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void PeriodicTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
bool PeriodicTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
this->scheduler = scheduler;
|
||||
this->loop = loop;
|
||||
|
||||
watcher = scheduler->installPeriodicEvent(loop, this, offset, intervall);
|
||||
if (watcher == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace triagens {
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup (Scheduler*, EventLoop);
|
||||
bool setup (Scheduler*, EventLoop);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
|
|
|
@ -260,6 +260,7 @@ SchedulerLibev::SchedulerLibev (size_t concurrency, int backend)
|
|||
: Scheduler(concurrency),
|
||||
_backend(backend) {
|
||||
|
||||
//_backend = 1;
|
||||
// setup lock
|
||||
SCHEDULER_INIT(&_watcherLock);
|
||||
|
||||
|
@ -530,28 +531,72 @@ EventToken SchedulerLibev::installSignalEvent (EventLoop loop, Task* task, int s
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EventToken SchedulerLibev::installSocketEvent (EventLoop loop, EventType type, Task* task, socket_t fd) {
|
||||
SocketWatcher* watcher = new SocketWatcher;
|
||||
watcher->loop = (struct ev_loop*) lookupLoop(loop);
|
||||
watcher->task = task;
|
||||
watcher->token = registerWatcher(watcher, EVENT_SOCKET_READ);
|
||||
#ifdef _WIN32
|
||||
|
||||
int flags = 0;
|
||||
// ..........................................................................
|
||||
// Windows likes to operate on SOCKET types (sort of handles) while libev
|
||||
// likes to operate on file descriptors
|
||||
// ..........................................................................
|
||||
|
||||
EventToken SchedulerLibev::installSocketEvent (EventLoop loop, EventType type, Task* task, socket_t socket) {
|
||||
SocketWatcher* watcher = new SocketWatcher;
|
||||
watcher->loop = (struct ev_loop*) lookupLoop(loop);
|
||||
watcher->task = task;
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (type & EVENT_SOCKET_READ) {
|
||||
flags |= EV_READ;
|
||||
if (type & EVENT_SOCKET_READ) {
|
||||
flags |= EV_READ;
|
||||
}
|
||||
|
||||
if (type & EVENT_SOCKET_WRITE) {
|
||||
flags |= EV_WRITE;
|
||||
}
|
||||
|
||||
// ..........................................................................
|
||||
// The problem we have here is that this opening of the fs handle may fail.
|
||||
// There is no mechanism to the calling function to report failure.
|
||||
// ..........................................................................
|
||||
LOGGER_TRACE << "attempting to convert socket handle to socket descriptor";
|
||||
int fd = _open_osfhandle (socket, 0);
|
||||
if (fd == -1) {
|
||||
LOGGER_ERROR << "could not convert socket handle to socket descriptor";
|
||||
delete watcher;
|
||||
abort();
|
||||
// Dr. O TODO: return to calling function
|
||||
return -1;
|
||||
}
|
||||
|
||||
watcher->token = registerWatcher(watcher, EVENT_SOCKET_READ);
|
||||
ev_io* w = (ev_io*) watcher;
|
||||
ev_io_init(w, socketCallback, fd, flags);
|
||||
ev_io_start(watcher->loop, w);
|
||||
return watcher->token;
|
||||
}
|
||||
#else
|
||||
EventToken SchedulerLibev::installSocketEvent (EventLoop loop, EventType type, Task* task, socket_t fd) {
|
||||
SocketWatcher* watcher = new SocketWatcher;
|
||||
watcher->loop = (struct ev_loop*) lookupLoop(loop);
|
||||
watcher->task = task;
|
||||
watcher->token = registerWatcher(watcher, EVENT_SOCKET_READ);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (type & EVENT_SOCKET_WRITE) {
|
||||
flags |= EV_WRITE;
|
||||
if (type & EVENT_SOCKET_READ) {
|
||||
flags |= EV_READ;
|
||||
}
|
||||
|
||||
if (type & EVENT_SOCKET_WRITE) {
|
||||
flags |= EV_WRITE;
|
||||
}
|
||||
|
||||
ev_io* w = (ev_io*) watcher;
|
||||
ev_io_init(w, socketCallback, fd, flags);
|
||||
ev_io_start(watcher->loop, w);
|
||||
|
||||
return watcher->token;
|
||||
}
|
||||
|
||||
ev_io* w = (ev_io*) watcher;
|
||||
ev_io_init(w, socketCallback, fd, flags);
|
||||
ev_io_start(watcher->loop, w);
|
||||
|
||||
return watcher->token;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
|
@ -657,7 +702,7 @@ void SchedulerLibev::rearmTimer (EventToken token, double timeout) {
|
|||
void* SchedulerLibev::lookupWatcher (EventToken token) {
|
||||
SCHEDULER_LOCK(&_watcherLock);
|
||||
|
||||
if (token >= _watchers.size()) {
|
||||
if (token >= (EventToken) _watchers.size()) {
|
||||
SCHEDULER_UNLOCK(&_watcherLock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -675,7 +720,7 @@ void* SchedulerLibev::lookupWatcher (EventToken token) {
|
|||
void* SchedulerLibev::lookupWatcher (EventToken token, EventType& type) {
|
||||
SCHEDULER_LOCK(&_watcherLock);
|
||||
|
||||
if (token >= _watchers.size()) {
|
||||
if (token >= (EventToken) _watchers.size()) {
|
||||
SCHEDULER_UNLOCK(&_watcherLock);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ bool SignalTask::addSignal (int signal) {
|
|||
// Task methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void SignalTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
bool SignalTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
this->scheduler = scheduler;
|
||||
this->loop = loop;
|
||||
|
||||
|
@ -91,6 +91,7 @@ void SignalTask::setup (Scheduler* scheduler, EventLoop loop) {
|
|||
for (set<int>::iterator i = signals.begin(); i != signals.end() && pos < MAX_SIGNALS; ++i, ++pos) {
|
||||
watcher[pos] = scheduler->installSignalEvent(loop, this, *i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace triagens {
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup (Scheduler*, EventLoop);
|
||||
bool setup (Scheduler*, EventLoop);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
|
|
|
@ -80,8 +80,8 @@ SocketTask::SocketTask (socket_t fd, double keepAliveTimeout)
|
|||
|
||||
SocketTask::~SocketTask () {
|
||||
if (commSocket != -1) {
|
||||
TRI_CLOSE(commSocket);
|
||||
//close(commSocket);
|
||||
TRI_CLOSE_SOCKET(commSocket);
|
||||
commSocket = -1;
|
||||
}
|
||||
|
||||
if (_writeBuffer != 0) {
|
||||
|
@ -149,11 +149,12 @@ void SocketTask::setKeepAliveTimeout (double timeout) {
|
|||
bool SocketTask::fillReadBuffer (bool& closed) {
|
||||
closed = false;
|
||||
|
||||
int nr = TRI_READ(commSocket, tmpReadBuffer, READ_BLOCK_SIZE);
|
||||
|
||||
int nr = TRI_READ_SOCKET(commSocket, tmpReadBuffer, READ_BLOCK_SIZE, 0);
|
||||
|
||||
|
||||
if (nr > 0) {
|
||||
_readBuffer->appendText(tmpReadBuffer, nr);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (nr == 0) {
|
||||
|
@ -164,6 +165,7 @@ bool SocketTask::fillReadBuffer (bool& closed) {
|
|||
return false;
|
||||
}
|
||||
else {
|
||||
|
||||
if (errno == EINTR) {
|
||||
return fillReadBuffer(closed);
|
||||
}
|
||||
|
@ -204,7 +206,7 @@ bool SocketTask::handleWrite (bool& closed, bool noWrite) {
|
|||
int nr = 0;
|
||||
|
||||
if (0 < len) {
|
||||
nr = TRI_WRITE(commSocket, _writeBuffer->begin() + writeLength, (int) len);
|
||||
nr = TRI_WRITE_SOCKET(commSocket, _writeBuffer->begin() + writeLength, (int) len, 0);
|
||||
|
||||
if (nr < 0) {
|
||||
if (errno == EINTR) {
|
||||
|
@ -406,20 +408,23 @@ bool SocketTask::hasWriteBuffer () const {
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SocketTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
bool SocketTask::setup (Scheduler* scheduler, EventLoop loop) {
|
||||
this->scheduler = scheduler;
|
||||
this->loop = loop;
|
||||
|
||||
watcher = scheduler->installAsyncEvent(loop, this);
|
||||
readWatcher = scheduler->installSocketEvent(loop, EVENT_SOCKET_READ, this, commSocket);
|
||||
writeWatcher = scheduler->installSocketEvent(loop, EVENT_SOCKET_WRITE, this, commSocket);
|
||||
|
||||
if (readWatcher == -1 || writeWatcher == -1) {
|
||||
return false;
|
||||
}
|
||||
// install timer for keep-alive timeout with some high default value
|
||||
keepAliveWatcher = scheduler->installTimerEvent(loop, this, 60.0);
|
||||
// and stop it immediately so it's not actively at the start
|
||||
scheduler->clearTimer(keepAliveWatcher);
|
||||
|
||||
tid = Thread::currentThreadId();
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
#include "Basics/Thread.h"
|
||||
#include "Statistics/StatisticsAgent.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "BasicsC/win-utils.h"
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- forward declarations
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -237,7 +241,7 @@ namespace triagens {
|
|||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup (Scheduler*, EventLoop);
|
||||
bool setup (Scheduler*, EventLoop);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
|
@ -295,7 +299,8 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
socket_t commSocket;
|
||||
|
||||
int fileDescriptor;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief keep-alive timeout in seconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace triagens {
|
|||
/// belongs to the loop parameter.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void setup (Scheduler*, EventLoop) = 0;
|
||||
virtual bool setup (Scheduler*, EventLoop) = 0;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief called to clear the callback information
|
||||
|
|
|
@ -49,7 +49,10 @@ void TaskManager::deleteTask (Task* task) {
|
|||
|
||||
|
||||
void TaskManager::setupTask (Task* task, Scheduler* scheduler, EventLoop loop) {
|
||||
task->setup(scheduler, loop);
|
||||
string name = task->getName();
|
||||
bool ok = task->setup(scheduler, loop);
|
||||
// TODO: respond when not ok
|
||||
(void) ok;
|
||||
}
|
||||
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue