diff --git a/arangod/RestHandler/RestDocumentHandler.cpp b/arangod/RestHandler/RestDocumentHandler.cpp index f7f3ca906f..2e86f48089 100644 --- a/arangod/RestHandler/RestDocumentHandler.cpp +++ b/arangod/RestHandler/RestDocumentHandler.cpp @@ -1192,7 +1192,7 @@ bool RestDocumentHandler::replaceDocument () { /// /// @RESTQUERYPARAMETERS /// -/// @RESTQUERYPARAM{keepNull,string,optional} +/// @RESTQUERYPARAM{keepNull,boolean,optional} /// If the intention is to delete existing attributes with the patch command, /// the URL query parameter `keepNull` can be used with a value of `false`. /// This will modify the behavior of the patch command to remove any attributes diff --git a/arangod/RestHandler/RestEdgeHandler.cpp b/arangod/RestHandler/RestEdgeHandler.cpp index 6d0d0ecadb..88a1d5f934 100644 --- a/arangod/RestHandler/RestEdgeHandler.cpp +++ b/arangod/RestHandler/RestEdgeHandler.cpp @@ -628,7 +628,7 @@ bool RestEdgeHandler::createDocumentCoordinator (string const& collname, /// /// @RESTQUERYPARAMETERS /// -/// @RESTQUERYPARAM{keepNull,string,optional} +/// @RESTQUERYPARAM{keepNull,boolean,optional} /// If the intention is to delete existing attributes with the patch command, /// the URL query parameter `keepNull` can be used with a value of `false`. /// This will modify the behavior of the patch command to remove any attributes diff --git a/js/actions/api-structure.js b/js/actions/api-structure.js index 2efe566eab..45a1c48587 100644 --- a/js/actions/api-structure.js +++ b/js/actions/api-structure.js @@ -1475,7 +1475,7 @@ function delete_api_structure (req, res) { /// /// @RESTQUERYPARAMETERS /// -/// @RESTQUERYPARAM{keepNull,string,optional} +/// @RESTQUERYPARAM{keepNull,boolean,optional} /// If the intention is to delete existing attributes with the patch command, /// the URL query parameter `keepNull` can be used with a value of `false`. /// This will modify the behavior of the patch command to remove any attributes diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/graph/abstractAdapter.js b/js/apps/system/aardvark/frontend/js/graphViewer/graph/abstractAdapter.js index f14fb1e0c1..e30f4ade5f 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/graph/abstractAdapter.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/graph/abstractAdapter.js @@ -118,6 +118,7 @@ function AbstractAdapter(nodes, edges, descendant, viewer, config) { } node.x = x || initialX.getStart(); node.y = y || initialY.getStart(); + node.weight = 1; nodes.push(node); node._outboundCounter = 0; node._inboundCounter = 0; diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/graph/eventDispatcher.js b/js/apps/system/aardvark/frontend/js/graphViewer/graph/eventDispatcher.js index 388593a6a0..ed63ad14ab 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/graph/eventDispatcher.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/graph/eventDispatcher.js @@ -181,7 +181,7 @@ function EventDispatcher(nodeShaper, edgeShaper, config) { } if (config.drag !== undefined) { if (eventlib.checkDragConfig(config.drag)) { - self.events.DRAG = new eventlib.Drag(config.drag); + self.events.DRAG = eventlib.Drag(config.drag); } } if (config.nodeEditor !== undefined) { diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js index 860cb4064d..75955acc53 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/arangoAdapterControls.js @@ -135,7 +135,7 @@ function ArangoAdapterControls(list, adapter) { idprefix, [{ type: "extendable", id: "attribute", - objects: prioList + objects: adapter.getPrioList() }], function () { var list = $("input[id^=" + idprefix + "attribute_]"), prios = []; diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js index e54f267681..e929071832 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js @@ -29,10 +29,8 @@ /// @author Michael Hackstein /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -/* Archive -function EventDispatcherControls(list, cursorIconBox, nodeShaper, edgeShaper, dispatcherConfig) { -*/ -function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) { + +function EventDispatcherControls(list, nodeShaper, edgeShaper, start, dispatcherConfig) { "use strict"; if (list === undefined) { @@ -44,6 +42,9 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) if (edgeShaper === undefined) { throw "The EdgeShaper has to be given."; } + if (start === undefined) { + throw "The Start callback has to be given."; + } var self = this, icons = { @@ -60,14 +61,6 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) appendToList = function(button) { list.appendChild(button); }, - createButton = function(title, callback) { - uiComponentsHelper.createButton( - list, - title, - "control_event_" + title, - callback - ); - }, createIcon = function(icon, title, callback) { var btn = uiComponentsHelper.createIconButton( icon, @@ -132,6 +125,7 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) dispatcher.events.CREATENODE(data, function(node) { $("#" + idprefix + "modal").modal('hide'); nodeShaper.reshapeNodes(); + start(); }, pos.x, pos.y)(); } ); @@ -197,19 +191,20 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) nodesUp = dispatcher.events.FINISHCREATEEDGE(function(edge){ edgeShaper.removeCursorFollowingEdge(); dispatcher.bind("svg", "mousemove", function(){}); + start(); }), svgUp = function() { dispatcher.events.CANCELCREATEEDGE(); edgeShaper.removeCursorFollowingEdge(); + dispatcher.bind("svg", "mousemove", function(){}); }; callbacks.nodes.startEdge = nodesDown; callbacks.nodes.endEdge = nodesUp; callbacks.svg.cancelEdge = svgUp; }, + createEditsCBs = function() { - var prefix = "control_event_edit", - idprefix = prefix + "_", - nodeCallback = function(n) { + var nodeCallback = function(n) { modalDialogHelper.createModalEditDialog( "Edit Node " + n._id, "control_event_node_edit_", @@ -236,14 +231,42 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) callbacks.nodes.edit = nodeCallback; callbacks.edges.edit = edgeCallback; }, + createDeleteCBs = function() { - callbacks.nodes.del = dispatcher.events.DELETENODE( - function() {} - ); - callbacks.edges.del = dispatcher.events.DELETEEDGE( - function() {} - ); + var nodeCallback = function(n) { + modalDialogHelper.createModalDeleteDialog( + "Delete Node " + n._id, + "control_event_node_delete_", + n, + function(n) { + dispatcher.events.DELETENODE(function() { + $("#control_event_node_delete_modal").modal('hide'); + nodeShaper.reshapeNodes(); + edgeShaper.reshapeEdges(); + start(); + })(n); + } + ); + }, + edgeCallback = function(e) { + modalDialogHelper.createModalDeleteDialog( + "Delete Edge " + e._id, + "control_event_edge_delete_", + e, + function(e) { + dispatcher.events.DELETEEDGE(function() { + $("#control_event_edge_delete_modal").modal('hide'); + nodeShaper.reshapeNodes(); + edgeShaper.reshapeEdges(); + start(); + })(e); + } + ); + }; + callbacks.nodes.del = nodeCallback; + callbacks.edges.del = edgeCallback; }, + createSpotCB = function() { callbacks.nodes.spot = dispatcher.events.EXPAND; }; @@ -400,13 +423,7 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, dispatcherConfig) this.addControlDelete = function() { var icon = icons.trash, callback = function() { - rebindNodes({click: dispatcher.events.DELETENODE(function() { - - })}); - rebindEdges({click: dispatcher.events.DELETEEDGE(function() { - - })}); - rebindSVG(); + self.rebindAll(self.deleteRebinds()); }; createIcon(icon, "delete", callback); }; diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerPreview.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerPreview.js index efaceeb594..ff2798f235 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerPreview.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerPreview.js @@ -111,9 +111,9 @@ function GraphViewerPreview(container, viewerConfig) { var toolbox = document.createElement("div"); dispatcherUI = new EventDispatcherControls( toolbox, - // mousePointerBox, viewer.nodeShaper, viewer.edgeShaper, + viewer.start, viewer.dispatcherConfig ); toolbox.id = "toolbox"; @@ -153,6 +153,7 @@ function GraphViewerPreview(container, viewerConfig) { // mousePointerBox, viewer.nodeShaper, viewer.edgeShaper, + viewer.start, viewer.dispatcherConfig ); }, @@ -212,4 +213,4 @@ function GraphViewerPreview(container, viewerConfig) { viewer.loadGraph("1"); parseActions(viewerConfig.actions); -} \ No newline at end of file +} diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js index b19f7695bc..8332ce10ee 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerUI.js @@ -326,6 +326,7 @@ function GraphViewerUI(container, adapterConfig, optWidth, optHeight, viewerConf toolbox, graphViewer.nodeShaper, graphViewer.edgeShaper, + graphViewer.start, graphViewer.dispatcherConfig ); toolbox.id = "toolbox"; diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerWidget.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerWidget.js index 75955eaa09..5702736717 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerWidget.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/graphViewerWidget.js @@ -112,9 +112,9 @@ function GraphViewerWidget(viewerConfig, startNode) { var toolbox = document.createElement("div"); dispatcherUI = new EventDispatcherControls( toolbox, - //mousePointerBox, viewer.nodeShaper, viewer.edgeShaper, + viewer.start, viewer.dispatcherConfig ); toolbox.id = "toolbox"; @@ -154,6 +154,7 @@ function GraphViewerWidget(viewerConfig, startNode) { // mousePointerBox, viewer.nodeShaper, viewer.edgeShaper, + viewer.start, viewer.dispatcherConfig ); }, diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/modalDialogHelper.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/modalDialogHelper.js index 63ca7ff1c2..151c60a458 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/modalDialogHelper.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/modalDialogHelper.js @@ -84,7 +84,6 @@ var modalDialogHelper = modalDialogHelper || {}; thirdCell.className = "actionCell"; thirdCell.appendChild(addRow); - addRow.id = idprefix + "new"; addRow.className = "graphViewer-icon-button gv-icon-small add"; @@ -296,6 +295,65 @@ var modalDialogHelper = modalDialogHelper || {}; for (i = 1; i < list.length; i++) { addNewLine(list[i]); } + }, + + modalContent = function(title, idprefix) { + // Create needed Elements + + var div = document.createElement("div"), + headerDiv = document.createElement("div"), + buttonDismiss = document.createElement("button"), + header = document.createElement("a"), + bodyDiv = document.createElement("div"), + bodyTable = document.createElement("table"); + + // Set Classnames and attributes. + div.id = idprefix + "modal"; + div.className = "modal hide fade"; + div.setAttribute("tabindex", "-1"); + div.setAttribute("role", "dialog"); + div.setAttribute("aria-labelledby", "myModalLabel"); + div.setAttribute("aria-hidden", true); + div.style.display = "none"; + div.onhidden = function() { + unbindSubmit(); + document.body.removeChild(div); + }; + + headerDiv.className = "modal-header"; + header.className = "arangoHeader"; + buttonDismiss.id = idprefix + "modal_dismiss"; + buttonDismiss.className = "close"; + buttonDismiss.dataDismiss = "modal"; + buttonDismiss.ariaHidden = "true"; + buttonDismiss.appendChild(document.createTextNode("×")); + + header.appendChild(document.createTextNode(title)); + + bodyDiv.className = "modal-body"; + + bodyTable.id = idprefix + "table"; + + // Append in correct ordering + div.appendChild(headerDiv); + div.appendChild(bodyDiv); + + headerDiv.appendChild(buttonDismiss); + headerDiv.appendChild(header); + + bodyDiv.appendChild(bodyTable); + + document.body.appendChild(div); + + buttonDismiss.onclick = function() { + unbindSubmit(); + $("#" + idprefix + "modal").modal('hide'); + }; + + return { + div: div, + bodyTable: bodyTable + }; }; insertModalRow = function(table, idprefix, o) { @@ -342,49 +400,17 @@ var modalDialogHelper = modalDialogHelper || {}; } return tr; }; - + + modalDialogHelper.modalDivTemplate = function (title, buttonTitle, idprefix, callback) { - // Create needed Elements - + buttonTitle = buttonTitle || "Switch"; - - var div = document.createElement("div"), - headerDiv = document.createElement("div"), - buttonDismiss = document.createElement("button"), - header = document.createElement("a"), - footerDiv = document.createElement("div"), - buttonCancel = document.createElement("button"), - buttonSubmit = document.createElement("button"), - bodyDiv = document.createElement("div"), - bodyTable = document.createElement("table"); - - // Set Classnames and attributes. - div.id = idprefix + "modal"; - div.className = "modal hide fade"; - div.setAttribute("tabindex", "-1"); - div.setAttribute("role", "dialog"); - div.setAttribute("aria-labelledby", "myModalLabel"); - div.setAttribute("aria-hidden", true); - div.style.display = "none"; - div.onhidden = function() { - unbindSubmit(); - document.body.removeChild(div); - }; - - headerDiv.className = "modal-header"; - header.className = "arangoHeader"; - buttonDismiss.id = idprefix + "modal_dismiss"; - buttonDismiss.className = "close"; - buttonDismiss.dataDismiss = "modal"; - buttonDismiss.ariaHidden = "true"; - buttonDismiss.appendChild(document.createTextNode("×")); - - header.appendChild(document.createTextNode(title)); - - bodyDiv.className = "modal-body"; - - bodyTable.id = idprefix + "table"; - + + var footerDiv = document.createElement("div"), + buttonCancel = document.createElement("button"), + buttonSubmit = document.createElement("button"), + content = modalContent(title, idprefix); + footerDiv.className = "modal-footer"; buttonCancel.id = idprefix + "cancel"; @@ -396,26 +422,11 @@ var modalDialogHelper = modalDialogHelper || {}; buttonSubmit.style.marginRight = "8px"; buttonSubmit.appendChild(document.createTextNode(buttonTitle)); - // Append in correct ordering - div.appendChild(headerDiv); - div.appendChild(bodyDiv); - div.appendChild(footerDiv); - - headerDiv.appendChild(buttonDismiss); - headerDiv.appendChild(header); - - bodyDiv.appendChild(bodyTable); - + content.div.appendChild(footerDiv); footerDiv.appendChild(buttonSubmit); footerDiv.appendChild(buttonCancel); - - document.body.appendChild(div); // Add click events - buttonDismiss.onclick = function() { - unbindSubmit(); - $("#" + idprefix + "modal").modal('hide'); - }; buttonCancel.onclick = function() { unbindSubmit(); $("#" + idprefix + "modal").modal('hide'); @@ -427,7 +438,7 @@ var modalDialogHelper = modalDialogHelper || {}; }; bindSubmit(buttonSubmit); // Return the table which has to be filled somewhere else - return bodyTable; + return content.bodyTable; }; modalDialogHelper.createModalDialog = function(title, idprefix, objects, callback) { @@ -440,6 +451,7 @@ var modalDialogHelper = modalDialogHelper || {}; modalDialogHelper.createModalChangeDialog = function(title, idprefix, objects, callback) { var table = modalDialogHelper.modalDivTemplate(title, "Change", idprefix, callback); + console.log(objects); _.each(objects, function(o) { insertModalRow(table, idprefix, o); }); @@ -454,9 +466,43 @@ var modalDialogHelper = modalDialogHelper || {}; createDialogWithObject(title, "Create", idprefix, object, callback); }; - modalDialogHelper.createModalViewDialog = function(title, idprefix, object, callback) { createViewWithObject(title, "Edit", idprefix, object, callback); }; + modalDialogHelper.createModalDeleteDialog = function(title, idprefix, object, callback) { + var footerDiv = document.createElement("div"), + buttonCancel = document.createElement("button"), + buttonSubmit = document.createElement("button"), + content = modalContent(title, idprefix); + + footerDiv.className = "modal-footer"; + + buttonCancel.id = idprefix + "cancel"; + buttonCancel.className = "btn btn-close btn-margin"; + buttonCancel.appendChild(document.createTextNode("Close")); + + buttonSubmit.id = idprefix + "submit"; + buttonSubmit.className = "btn btn-danger"; + buttonSubmit.style.marginRight = "8px"; + buttonSubmit.appendChild(document.createTextNode("Delete")); + + content.div.appendChild(footerDiv); + footerDiv.appendChild(buttonSubmit); + footerDiv.appendChild(buttonCancel); + + // Add click events + buttonCancel.onclick = function() { + unbindSubmit(); + $("#" + idprefix + "modal").modal('hide'); + }; + buttonSubmit.onclick = function() { + unbindSubmit(); + callback(object); + $("#" + idprefix + "modal").modal('hide'); + }; + bindSubmit(buttonSubmit); + $("#" + idprefix + "modal").modal('show'); + }; + }()); diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js index baa91c2777..7c367a531e 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specEvents/eventDispatcherUISpec.js @@ -40,8 +40,9 @@ describe('Event Dispatcher UI', function () { var svg, dispatcherUI, list, $list, - nodeShaper, edgeShaper, layouter, - nodes, edges, adapter, + nodeShaper, edgeShaper, layouter, + nodes, edges, adapter, + start, //mousePointerbox, addSpies = function() { @@ -55,11 +56,15 @@ spyOn(adapter, "loadNode"); spyOn(adapter, "expandCommunity"); spyOn(adapter, "explore"); + spyOn(start, "cb"); }; beforeEach(function () { + start = { + cb: function() {} + }; nodes = [{ _id: 1, x: 3, @@ -163,7 +168,7 @@ */ dispatcherUI = new EventDispatcherControls( - list, nodeShaper, edgeShaper, completeConfig + list, nodeShaper, edgeShaper, start.cb, completeConfig ); spyOn(nodeShaper, "changeTo").andCallThrough(); @@ -216,6 +221,10 @@ var e = new EventDispatcherControls(list, nodeShaper); }).toThrow("The EdgeShaper has to be given."); + expect(function() { + var e = new EventDispatcherControls(list, nodeShaper, edgeShaper); + }).toThrow("The Start callback has to be given."); + }); it('should be able to add a new node control to the list', function() { @@ -600,14 +609,15 @@ }); }); - it('should be able to add a delete control to the list', function() { - runs(function() { + describe("delete control", function() { + + beforeEach(function() { dispatcherUI.addControlDelete(); - - expect($("#control_event_list #control_event_delete").length).toEqual(1); - helper.simulateMouseEvent("click", "control_event_delete"); - + }); + + it("should be added to the list", function() { + expect($("#control_event_list #control_event_delete").length).toEqual(1); expect(edgeShaper.changeTo).toHaveBeenCalledWith({ actions: { reset: true, @@ -621,24 +631,27 @@ click: jasmine.any(Function) } }); - - //expect(mousePointerbox.className).toEqual("mousepointer icon-trash"); - - helper.simulateMouseEvent("click", "1"); - + }); + + it("should ask for permission and delete nodes", function() { + var id = "1"; + helper.simulateMouseEvent("click", id); + helper.simulateMouseEvent("click", "control_event_node_delete_submit"); expect(adapter.deleteNode).toHaveBeenCalledWith( nodes[0], jasmine.any(Function) ); - - helper.simulateMouseEvent("click", "1-2"); - + }); + + it("should ask for permission and delete nodes", function() { + var id = "1-2"; + helper.simulateMouseEvent("click", id); + helper.simulateMouseEvent("click", "control_event_edge_delete_submit"); expect(adapter.deleteEdge).toHaveBeenCalledWith( edges[0], jasmine.any(Function) ); - - }); + }); }); describe('the connect control', function() { diff --git a/js/apps/system/aardvark/test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js b/js/apps/system/aardvark/test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js index fb3ddcbe72..6ead5d4339 100644 --- a/js/apps/system/aardvark/test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js +++ b/js/apps/system/aardvark/test/specs/graphViewer/specGraphViewer/graphViewerPreviewSpec.js @@ -72,7 +72,7 @@ cont.offsetWidth, cont.offsetHeight, { - type: "preview", + type: "preview", width: width, height: height }, @@ -138,7 +138,7 @@ ui; spy.shapes = oldShapes; - ui = new GraphViewerPreview(cont, config); + ui = new GraphViewerPreview(cont, config); expect(window.NodeShaper).wasCalledWith( jasmine.any(Object), { @@ -494,4 +494,4 @@ }); -}()); \ No newline at end of file +}()); diff --git a/js/common/bootstrap/module-internal.js b/js/common/bootstrap/module-internal.js index b034c9f85c..a717518f86 100644 --- a/js/common/bootstrap/module-internal.js +++ b/js/common/bootstrap/module-internal.js @@ -19,7 +19,7 @@ COLOR_BOLD_MAGENTA, PRETTY_PRINT, VALGRIND, VERSION, UPGRADE, BYTES_SENT_DISTRIBUTION, BYTES_RECEIVED_DISTRIBUTION, CONNECTION_TIME_DISTRIBUTION, REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, THREAD_NUMBER, LOGFILE_PATH, - SYS_PLATFORM */ + SYS_PLATFORM, SYS_EXECUTE_EXTERNAL, SYS_STATUS_EXTERNAL, SYS_KILL_EXTERNAL */ //////////////////////////////////////////////////////////////////////////////// /// @brief module "internal" @@ -662,6 +662,33 @@ delete SYS_HTTP_STATISTICS; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates an external process +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_EXECUTE_EXTERNAL !== "undefined") { + exports.executeExternal = SYS_EXECUTE_EXTERNAL; + delete SYS_EXECUTE_EXTERNAL; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief kills an external process +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_KILL_EXTERNAL !== "undefined") { + exports.killExternal = SYS_KILL_EXTERNAL; + delete SYS_KILL_EXTERNAL; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks a status of an external process +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_STATUS_EXTERNAL !== "undefined") { + exports.statusExternal = SYS_STATUS_EXTERNAL; + delete SYS_STATUS_EXTERNAL; + } + // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- diff --git a/lib/BasicsC/process-utils.c b/lib/BasicsC/process-utils.c index 297f10eae5..6a47c16cd4 100644 --- a/lib/BasicsC/process-utils.c +++ b/lib/BasicsC/process-utils.c @@ -42,17 +42,13 @@ #endif #include "BasicsC/tri-strings.h" +#include "BasicsC/locks.h" #include "BasicsC/logging.h" // ----------------------------------------------------------------------------- // --SECTION-- private types // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief contains all data documented by "proc" /// @@ -124,19 +120,10 @@ process_state_t; #endif -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief original process name //////////////////////////////////////////////////////////////////////////////// @@ -178,18 +165,142 @@ static bool MustFreeEnvironment = false; static size_t MaximalProcessTitleSize = 0; //////////////////////////////////////////////////////////////////////////////// -/// @} +/// @brief all external processes //////////////////////////////////////////////////////////////////////////////// +static TRI_vector_pointer_t ExternalProcesses; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lock for protected access to vector ExternalProcesses +//////////////////////////////////////////////////////////////////////////////// + +static TRI_mutex_t ExternalProcessesLock; + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates pipe pair +//////////////////////////////////////////////////////////////////////////////// + +static bool CreatePipes (int* pipe_server_to_child, + int* pipe_child_to_server) { + + if (pipe(pipe_server_to_child) == -1) { + LOG_ERROR("cannot create pipe"); + return false; + } + + if (pipe(pipe_child_to_server) == -1) { + LOG_ERROR("cannot create pipe"); + + close(pipe_server_to_child[0]); + close(pipe_server_to_child[1]); + + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief starts external process +//////////////////////////////////////////////////////////////////////////////// + +static void StartExternalProcess (TRI_external_t* external, bool usePipes) { + int pipe_server_to_child[2]; + int pipe_child_to_server[2]; + int processPid; + bool ok; + + if (usePipes) { + ok = CreatePipes(pipe_server_to_child, pipe_child_to_server); + + if (! ok) { + external->_status = TRI_EXT_PIPE_FAILED; + return; + } + } + + processPid = fork(); + + // child process + if (processPid == 0) { + + // set stdin and stdout of child process + if (usePipes) { + dup2(pipe_server_to_child[0], 0); + dup2(pipe_child_to_server[1], 1); + + fcntl(0, F_SETFD, 0); + fcntl(1, F_SETFD, 0); + fcntl(2, F_SETFD, 0); + + // close pipes + close(pipe_server_to_child[0]); + close(pipe_server_to_child[1]); + close(pipe_child_to_server[0]); + close(pipe_child_to_server[1]); + } + else { + close(0); + fcntl(1, F_SETFD, 0); + fcntl(2, F_SETFD, 0); + } + + // ignore signals in worker process + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + + // execute worker + execv(external->_executable, external->_arguments); + + // should not happen + LOG_ERROR("execution of '%s' failed with %d", external->_executable, errno); + + exit(1); + } + + // parent + if (processPid == -1) { + LOG_ERROR("fork failed"); + + if (usePipes) { + close(pipe_server_to_child[0]); + close(pipe_server_to_child[1]); + close(pipe_child_to_server[0]); + close(pipe_child_to_server[1]); + } + + external->_status = TRI_EXT_FORK_FAILED; + return; + } + + LOG_DEBUG("fork succeeded %d", processPid); + + if (usePipes) { + close(pipe_server_to_child[0]); + close(pipe_child_to_server[1]); + + external->_writePipe = pipe_server_to_child[1]; + external->_readPipe = pipe_child_to_server[0]; + } + else { + external->_writePipe = -1; + external->_readPipe = -1; + } + + external->_pid = processPid; + external->_status = TRI_EXT_RUNNING; +} + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief converts usec and sec into seconds //////////////////////////////////////////////////////////////////////////////// @@ -472,22 +583,148 @@ void TRI_SetProcessTitle (char const* title) { } //////////////////////////////////////////////////////////////////////////////// -/// @} +/// @brief starts an external process //////////////////////////////////////////////////////////////////////////////// +TRI_external_id_t TRI_CreateExternalProcess (const char* executable, + const char** arguments, + size_t n) { + TRI_external_t* external; + TRI_external_id_t pid; + size_t i; + + // create the external structure + external = TRI_Allocate(TRI_CORE_MEM_ZONE, sizeof(TRI_external_t), true); + + external->_executable = TRI_DuplicateString(executable); + external->_numberArguments = n; + + external->_arguments = TRI_Allocate(TRI_CORE_MEM_ZONE, (n + 2) * sizeof(char*), true); + external->_arguments[0] = TRI_DuplicateString(executable); + + for (i = 0; i < n; ++i) { + external->_arguments[i + 1] = TRI_DuplicateString(arguments[i]); + } + + external->_arguments[n + 1] = NULL; + external->_status = TRI_EXT_NOT_STARTED; + + StartExternalProcess(external, false); + + TRI_LockMutex(&ExternalProcessesLock); + TRI_PushBackVectorPointer(&ExternalProcesses, external); + pid = external->_pid; + TRI_UnlockMutex(&ExternalProcessesLock); + + return pid; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the status of an external process +//////////////////////////////////////////////////////////////////////////////// + +TRI_external_status_t TRI_CheckExternalProcess (pid_t pid) { + TRI_external_status_t status; + TRI_external_t* external; + int loc; + int opts; + pid_t res; + size_t i; + + TRI_LockMutex(&ExternalProcessesLock); + + status._status = TRI_EXT_NOT_FOUND; + status._exitStatus = 0; + + for (i = 0; i < ExternalProcesses._length; ++i) { + external = TRI_AtVectorPointer(&ExternalProcesses, i); + + if (external->_pid == pid) { + break; + } + } + + if (i == ExternalProcesses._length) { + TRI_UnlockMutex(&ExternalProcessesLock); + return status; + } + + if (external->_status == TRI_EXT_RUNNING || external->_status == TRI_EXT_STOPPED) { + opts = WNOHANG | WUNTRACED; + res = waitpid(external->_pid, &loc, opts); + + if (res == 0) { + external->_exitStatus = 0; + } + else if (WIFEXITED(loc)) { + external->_status = TRI_EXT_TERMINATED; + external->_exitStatus = WEXITSTATUS(loc); + } + else if (WIFSIGNALED(loc)) { + external->_status = TRI_EXT_ABORTED; + external->_exitStatus = 0; + } + else if (WIFSTOPPED(loc)) { + external->_status = TRI_EXT_STOPPED; + external->_exitStatus = 0; + } + } + + status._status = external->_status; + status._exitStatus = external->_exitStatus; + + TRI_UnlockMutex(&ExternalProcessesLock); + return status; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief kills an external process +//////////////////////////////////////////////////////////////////////////////// + +void TRI_KillExternalProcess (pid_t pid) { + TRI_external_t* external; + size_t i; + + TRI_LockMutex(&ExternalProcessesLock); + + for (i = 0; i < ExternalProcesses._length; ++i) { + external = TRI_AtVectorPointer(&ExternalProcesses, i); + + if (external->_pid == pid) { + break; + } + } + + if (i == ExternalProcesses._length) { + TRI_UnlockMutex(&ExternalProcessesLock); + return; + } + + if (external->_status == TRI_EXT_RUNNING || external->_status == TRI_EXT_STOPPED) { + int val = kill(external->_pid , SIGKILL); + + if (val) { + external->_status = TRI_EXT_KILL_FAILED; + } + else { + TRI_RemoveVectorPointer(&ExternalProcesses, i); + } + } + else { + TRI_RemoveVectorPointer(&ExternalProcesses, i); + } + + TRI_UnlockMutex(&ExternalProcessesLock); +} + // ----------------------------------------------------------------------------- // --SECTION-- MODULE // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- -// --SECTION-- public functions +// --SECTION-- modules initialisation // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief initialises the process components //////////////////////////////////////////////////////////////////////////////// @@ -500,10 +737,13 @@ void TRI_InitialiseProcess (int argc, char* argv[]) { ProcessName = TRI_DuplicateString(argv[0]); ARGC = argc; ARGV = argv; + + TRI_InitVectorPointer(&ExternalProcesses, TRI_CORE_MEM_ZONE); + TRI_InitMutex(&ExternalProcessesLock); } //////////////////////////////////////////////////////////////////////////////// -/// @brief shut downs the process components +/// @brief shuts down the process components //////////////////////////////////////////////////////////////////////////////// void TRI_ShutdownProcess () { @@ -523,11 +763,14 @@ void TRI_ShutdownProcess () { TRI_Free(TRI_CORE_MEM_ZONE, environ); } #endif + + TRI_DestroyVectorPointer(&ExternalProcesses); + TRI_DestroyMutex(&ExternalProcessesLock); } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor diff --git a/lib/BasicsC/process-utils.h b/lib/BasicsC/process-utils.h index 0f94a5bcfe..04ce8398bc 100644 --- a/lib/BasicsC/process-utils.h +++ b/lib/BasicsC/process-utils.h @@ -40,30 +40,16 @@ extern "C" { // --SECTION-- public constants // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief invalid process //////////////////////////////////////////////////////////////////////////////// #define INVALID_PROCESS (0) -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public types // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief returns information about the process //////////////////////////////////////////////////////////////////////////////// @@ -81,18 +67,61 @@ typedef struct TRI_process_info_s { TRI_process_info_t; //////////////////////////////////////////////////////////////////////////////// -/// @} +/// @brief status of an external process //////////////////////////////////////////////////////////////////////////////// +typedef enum { + TRI_EXT_NOT_STARTED = 0, // not yet started + TRI_EXT_PIPE_FAILED = 1, // pipe before start failed + TRI_EXT_FORK_FAILED = 2, // fork failed + TRI_EXT_RUNNING = 3, // running + TRI_EXT_NOT_FOUND = 4, // unknown pid + TRI_EXT_TERMINATED = 5, // process has terminated normally + TRI_EXT_ABORTED = 6, // process has terminated abnormally + TRI_EXT_STOPPED = 7, // process has been stopped + TRI_EXT_KILL_FAILED = 8, // kill failed +} +TRI_external_status_e; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief identifier of an external process +//////////////////////////////////////////////////////////////////////////////// + +typedef pid_t TRI_external_id_t; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief external process description +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_external_s { + char* _executable; + size_t _numberArguments; + char** _arguments; + + TRI_external_id_t _pid; + + int _readPipe; + int _writePipe; + + TRI_external_status_e _status; + int _exitStatus; +} +TRI_external_t; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief external process status +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_external_status_s { + TRI_external_status_e _status; + int _exitStatus; +} +TRI_external_status_t; + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief converts usec and sec into seconds //////////////////////////////////////////////////////////////////////////////// @@ -132,22 +161,33 @@ uint64_t TRI_ProcessSize (TRI_pid_t pid); void TRI_SetProcessTitle (char const* title); //////////////////////////////////////////////////////////////////////////////// -/// @} +/// @brief starts an external process //////////////////////////////////////////////////////////////////////////////// +TRI_external_id_t TRI_CreateExternalProcess (const char* executable, + const char** arguments, + size_t n); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the status of an external process +//////////////////////////////////////////////////////////////////////////////// + +TRI_external_status_t TRI_CheckExternalProcess (pid_t pid); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief kills an external process +//////////////////////////////////////////////////////////////////////////////// + +void TRI_KillExternalProcess (pid_t pid); + // ----------------------------------------------------------------------------- // --SECTION-- MODULE // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- -// --SECTION-- public functions +// --SECTION-- modules initialisation // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup SystemProcess -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief initialises the process components //////////////////////////////////////////////////////////////////////////////// @@ -160,16 +200,16 @@ void TRI_InitialiseProcess (int argc, char* argv[]); void TRI_ShutdownProcess (void); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - #ifdef __cplusplus } #endif #endif +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" diff --git a/lib/BasicsC/socket-utils.c b/lib/BasicsC/socket-utils.c index d580d9ae92..4665a4b111 100644 --- a/lib/BasicsC/socket-utils.c +++ b/lib/BasicsC/socket-utils.c @@ -41,26 +41,12 @@ #include "BasicsC/logging.h" #include "BasicsC/locks.h" -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Sockets -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Sockets -/// @{ +/// @brief closes a socket //////////////////////////////////////////////////////////////////////////////// int TRI_closesocket(TRI_socket_t s) { @@ -173,24 +159,14 @@ bool TRI_SetNonBlockingSocket (TRI_socket_t s) { #endif - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- MODULE // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- -// --SECTION-- public functions +// --SECTION-- modules initialisation // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Sockets -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief initialises the sockets components //////////////////////////////////////////////////////////////////////////////// @@ -205,9 +181,9 @@ void TRI_InitialiseSockets (void) { void TRI_ShutdownSockets (void) { } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor diff --git a/lib/Rest/EndpointIp.cpp b/lib/Rest/EndpointIp.cpp index e3c7aebdfe..be6a4eec42 100644 --- a/lib/Rest/EndpointIp.cpp +++ b/lib/Rest/EndpointIp.cpp @@ -45,11 +45,6 @@ using namespace triagens::rest; // --SECTION-- static initialisers // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief set port if none specified //////////////////////////////////////////////////////////////////////////////// @@ -62,19 +57,10 @@ const uint16_t EndpointIp::_defaultPort = 8529; const std::string EndpointIp::_defaultHost = "127.0.0.1"; -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief creates an IP socket endpoint //////////////////////////////////////////////////////////////////////////////// @@ -92,7 +78,7 @@ EndpointIp::EndpointIp (const Endpoint::EndpointType type, } //////////////////////////////////////////////////////////////////////////////// -/// @brief destroys an IPv4 socket endpoint +/// @brief destroys an IP socket endpoint //////////////////////////////////////////////////////////////////////////////// EndpointIp::~EndpointIp () { @@ -101,17 +87,12 @@ EndpointIp::~EndpointIp () { } } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ +/// @brief connects a socket //////////////////////////////////////////////////////////////////////////////// TRI_socket_t EndpointIp::connectSocket (const struct addrinfo* aip, @@ -122,8 +103,8 @@ TRI_socket_t EndpointIp::connectSocket (const struct addrinfo* aip, char serv[NI_MAXSERV]; if (::getnameinfo(aip->ai_addr, (socklen_t) aip->ai_addrlen, - host, sizeof(host), - serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { LOG_TRACE("bind to address '%s', port %d", host, (int) _port); } @@ -214,19 +195,10 @@ TRI_socket_t EndpointIp::connectSocket (const struct addrinfo* aip, return _socket; } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public methods // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief connect the endpoint //////////////////////////////////////////////////////////////////////////////// @@ -351,6 +323,7 @@ TRI_socket_t EndpointIp::connect (double connectTimeout, double requestTimeout) return listenSocket; } + #endif //////////////////////////////////////////////////////////////////////////////// @@ -388,9 +361,9 @@ bool EndpointIp::initIncoming (TRI_socket_t incoming) { return setSocketFlags(incoming); } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor diff --git a/lib/Rest/EndpointIp.h b/lib/Rest/EndpointIp.h index 6d94204de5..a1f07817e7 100644 --- a/lib/Rest/EndpointIp.h +++ b/lib/Rest/EndpointIp.h @@ -31,7 +31,6 @@ #include "Rest/Endpoint.h" - namespace triagens { namespace rest { @@ -45,17 +44,11 @@ namespace triagens { // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - - protected: - //////////////////////////////////////////////////////////////////////////////// /// @brief creates an endpoint //////////////////////////////////////////////////////////////////////////////// + protected: EndpointIp (const EndpointType, const DomainType, const EncryptionType, @@ -64,27 +57,17 @@ namespace triagens { const std::string&, const uint16_t); - public: - //////////////////////////////////////////////////////////////////////////////// /// @brief destroys an endpoint //////////////////////////////////////////////////////////////////////////////// + public: ~EndpointIp (); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public variables // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - public: //////////////////////////////////////////////////////////////////////////////// @@ -99,20 +82,10 @@ namespace triagens { static const string _defaultHost; -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - - // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - private: //////////////////////////////////////////////////////////////////////////////// @@ -121,19 +94,10 @@ namespace triagens { TRI_socket_t connectSocket (const struct addrinfo*, double, double); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public methods // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - public: //////////////////////////////////////////////////////////////////////////////// @@ -170,19 +134,10 @@ namespace triagens { return _host; } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - private: //////////////////////////////////////////////////////////////////////////////// @@ -196,11 +151,6 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// uint16_t _port; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - }; } @@ -208,6 +158,10 @@ namespace triagens { #endif +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" diff --git a/lib/Rest/EndpointIpV4.cpp b/lib/Rest/EndpointIpV4.cpp index f700936586..863d5c5669 100644 --- a/lib/Rest/EndpointIpV4.cpp +++ b/lib/Rest/EndpointIpV4.cpp @@ -40,11 +40,6 @@ using namespace triagens::rest; // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief creates an IPv4 socket endpoint //////////////////////////////////////////////////////////////////////////////// @@ -65,9 +60,9 @@ EndpointIpV4::EndpointIpV4 (const Endpoint::EndpointType type, EndpointIpV4::~EndpointIpV4 () { } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor diff --git a/lib/Rest/EndpointIpV4.h b/lib/Rest/EndpointIpV4.h index 7c1eff5a7e..5959b760b3 100644 --- a/lib/Rest/EndpointIpV4.h +++ b/lib/Rest/EndpointIpV4.h @@ -44,11 +44,6 @@ namespace triagens { // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - public: //////////////////////////////////////////////////////////////////////////////// @@ -68,19 +63,10 @@ namespace triagens { ~EndpointIpV4 (); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public methods // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup Rest -/// @{ -//////////////////////////////////////////////////////////////////////////////// - public: //////////////////////////////////////////////////////////////////////////////// @@ -98,19 +84,16 @@ namespace triagens { string getHostString () const { return getHost() + ':' + triagens::basics::StringUtils::itoa(getPort()); } - - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - }; - } } #endif +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index e34441eede..1b831d7412 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -458,6 +458,7 @@ static v8::Handle JS_Download (v8::Arguments const& argv) { } v8::Handle options = v8::Handle::Cast(argv[2]); + if (options.IsEmpty()) { TRI_V8_EXCEPTION_USAGE(scope, signature); } @@ -647,11 +648,12 @@ static v8::Handle JS_Download (v8::Arguments const& argv) { map::const_iterator it; v8::Handle headers = v8::Object::New(); + for (it = responseHeaders.begin(); it != responseHeaders.end(); ++it) { headers->Set(v8::String::New((*it).first.c_str()), v8::String::New((*it).second.c_str())); } - result->Set(v8::String::New("headers"), headers); + result->Set(v8::String::New("headers"), headers); if (returnBodyOnError || (returnCode >= 200 && returnCode <= 299)) { try { @@ -2374,6 +2376,162 @@ static v8::Handle JS_HttpStatistics (v8::Arguments const& argv) { return scope.Close(result); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a external program +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ExecuteExternal (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the arguments + if (2 < argv.Length() || argv.Length() < 1) { + TRI_V8_EXCEPTION_USAGE(scope, "executeExternal(, [])"); + } + + TRI_Utf8ValueNFC name(TRI_UNKNOWN_MEM_ZONE, argv[0]); + + if (*name == 0) { + TRI_V8_TYPE_ERROR(scope, " must be a string"); + } + + char** arguments = 0; + size_t n = 0; + + if (2 <= argv.Length()) { + v8::Handle a = argv[1]; + + if (a->IsArray()) { + v8::Handle arr = v8::Handle::Cast(a); + + n = arr->Length(); + arguments = (char**) TRI_Allocate(TRI_CORE_MEM_ZONE, n * sizeof(char*), false); + + for (size_t i = 0; i < n; ++i) { + TRI_Utf8ValueNFC arg(TRI_UNKNOWN_MEM_ZONE, arr->Get(i)); + + if (*arg == 0) { + arguments[i] = TRI_DuplicateString(""); + } + else { + arguments[i] = TRI_DuplicateString(*arg); + } + } + } + else { + n = 1; + arguments = (char**) TRI_Allocate(TRI_CORE_MEM_ZONE, n * sizeof(char*), false); + + TRI_Utf8ValueNFC arg(TRI_UNKNOWN_MEM_ZONE, a); + + if (*arg == 0) { + arguments[0] = TRI_DuplicateString(""); + } + else { + arguments[0] = TRI_DuplicateString(*arg); + } + } + } + + TRI_external_id_t external = TRI_CreateExternalProcess(*name, (const char**) arguments, n); + + if (arguments != 0) { + for (size_t i = 0; i < n; ++i) { + TRI_FreeString(TRI_CORE_MEM_ZONE, arguments[i]); + } + + TRI_Free(TRI_CORE_MEM_ZONE, arguments); + } + + // return the result + return scope.Close(v8::Number::New(external)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the status of an external process +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_StatusExternal (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the arguments + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "statusExternal()"); + } + + TRI_external_id_t pid = TRI_ObjectToUInt64(argv[0], true); + TRI_external_status_t external = TRI_CheckExternalProcess(pid); + + v8::Handle result = v8::Object::New(); + const char* status = "UNKNOWN"; + + switch (external._status) { + case TRI_EXT_NOT_STARTED: status = "NOT-STARTED"; break; + case TRI_EXT_PIPE_FAILED: status = "FAILED"; break; + case TRI_EXT_FORK_FAILED: status = "FAILED"; break; + case TRI_EXT_RUNNING: status = "RUNNING"; break; + case TRI_EXT_NOT_FOUND: status = "NOT-FOUND"; break; + case TRI_EXT_TERMINATED: status = "TERMINATED"; break; + case TRI_EXT_ABORTED: status = "ABORTED"; break; + case TRI_EXT_STOPPED: status = "STOPPED"; break; + case TRI_EXT_KILL_FAILED: status = "ZOMBIE"; break; + } + + result->Set(v8::String::New("status"), v8::String::New(status)); + + if (external._status == TRI_EXT_TERMINATED) { + result->Set(v8::String::New("exit"), v8::Number::New(external._exitStatus)); + } + + // return the result + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief kills an external process +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KillExternal (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the arguments + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "killExternal()"); + } + + TRI_external_id_t pid = TRI_ObjectToUInt64(argv[0], true); + + TRI_KillExternalProcess(pid); + + // return the result + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks if a port is available +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_TestPort (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the arguments + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "testPort(
)"); + } + + string address = TRI_ObjectToString(argv[0]); + Endpoint* endpoint = Endpoint::serverFactory(address); + TRI_socket_t s = endpoint->connect(1, 1); + + if (s.fileDescriptor == 0) { + endpoint->disconnect(); + } + + delete endpoint; + + // return the result + return scope.Close(v8::Boolean::New(s.fileDescriptor != 0)); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief ArangoError //////////////////////////////////////////////////////////////////////////////// @@ -2720,7 +2878,7 @@ v8::Handle TRI_CreateErrorObject (int errorNumber, string const& mes } //////////////////////////////////////////////////////////////////////////////// -/// @brief normalize a v8 object +/// @brief normalizes a v8 object //////////////////////////////////////////////////////////////////////////////// v8::Handle TRI_normalize_V8_Obj (v8::Handle obj) { @@ -2856,20 +3014,22 @@ void TRI_InitV8Utils (v8::Handle context, TRI_AddGlobalFunctionVocbase(context, "SYS_BASE64DECODE", JS_Base64Decode); TRI_AddGlobalFunctionVocbase(context, "SYS_BASE64ENCODE", JS_Base64Encode); + TRI_AddGlobalFunctionVocbase(context, "SYS_CHECK_AND_MARK_NONCE", JS_MarkNonce); TRI_AddGlobalFunctionVocbase(context, "SYS_CLIENT_STATISTICS", JS_ClientStatistics); + TRI_AddGlobalFunctionVocbase(context, "SYS_CREATE_NONCE", JS_CreateNonce); TRI_AddGlobalFunctionVocbase(context, "SYS_DOWNLOAD", JS_Download); TRI_AddGlobalFunctionVocbase(context, "SYS_EXECUTE", JS_Execute); + TRI_AddGlobalFunctionVocbase(context, "SYS_EXECUTE_EXTERNAL", JS_ExecuteExternal); + TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_ALPHA_NUMBERS", JS_RandomAlphaNum); + TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_NUMBERS", JS_RandomNumbers); + TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_SALT", JS_RandomSalt); TRI_AddGlobalFunctionVocbase(context, "SYS_GETLINE", JS_Getline); + TRI_AddGlobalFunctionVocbase(context, "SYS_HTTP_STATISTICS", JS_HttpStatistics); + TRI_AddGlobalFunctionVocbase(context, "SYS_KILL_EXTERNAL", JS_KillExternal); TRI_AddGlobalFunctionVocbase(context, "SYS_LOAD", JS_Load); TRI_AddGlobalFunctionVocbase(context, "SYS_LOG", JS_Log); TRI_AddGlobalFunctionVocbase(context, "SYS_LOG_LEVEL", JS_LogLevel); TRI_AddGlobalFunctionVocbase(context, "SYS_MD5", JS_Md5); - TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_NUMBERS", JS_RandomNumbers); - TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_ALPHA_NUMBERS", JS_RandomAlphaNum); - TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_SALT", JS_RandomSalt); - TRI_AddGlobalFunctionVocbase(context, "SYS_HTTP_STATISTICS", JS_HttpStatistics); - TRI_AddGlobalFunctionVocbase(context, "SYS_CREATE_NONCE", JS_CreateNonce); - TRI_AddGlobalFunctionVocbase(context, "SYS_CHECK_AND_MARK_NONCE", JS_MarkNonce); TRI_AddGlobalFunctionVocbase(context, "SYS_OUTPUT", JS_Output); TRI_AddGlobalFunctionVocbase(context, "SYS_PARSE", JS_Parse); TRI_AddGlobalFunctionVocbase(context, "SYS_PROCESS_STATISTICS", JS_ProcessStatistics); @@ -2880,6 +3040,8 @@ void TRI_InitV8Utils (v8::Handle context, TRI_AddGlobalFunctionVocbase(context, "SYS_SERVER_STATISTICS", JS_ServerStatistics); TRI_AddGlobalFunctionVocbase(context, "SYS_SHA256", JS_Sha256); TRI_AddGlobalFunctionVocbase(context, "SYS_SPRINTF", JS_SPrintF); + TRI_AddGlobalFunctionVocbase(context, "SYS_STATUS_EXTERNAL", JS_StatusExternal); + TRI_AddGlobalFunctionVocbase(context, "SYS_TEST_PORT", JS_TestPort); TRI_AddGlobalFunctionVocbase(context, "SYS_TIME", JS_Time); TRI_AddGlobalFunctionVocbase(context, "SYS_WAIT", JS_Wait);