diff options
author | Michael Goulish <mgoulish@apache.org> | 2009-08-03 18:07:12 +0000 |
---|---|---|
committer | Michael Goulish <mgoulish@apache.org> | 2009-08-03 18:07:12 +0000 |
commit | 4b9c8a4a7eeb9d4436122b661e885e2abae85671 (patch) | |
tree | 94b294f083fb569d6519af19cb2db0df9cefc99d | |
parent | d42e5f77a4860e8788fd65494979fc700b45690d (diff) | |
download | qpid-python-4b9c8a4a7eeb9d4436122b661e885e2abae85671.tar.gz |
qrsh initial checkin.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@800481 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | qpid/cpp/src/tests/Makefile.am | 12 | ||||
-rw-r--r-- | qpid/cpp/src/tests/qrsh.cpp | 166 | ||||
-rw-r--r-- | qpid/cpp/src/tests/qrsh_run.cpp | 321 | ||||
-rw-r--r-- | qpid/cpp/src/tests/qrsh_server.cpp | 1062 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/10_all | 30 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/1_remote_run | 26 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/2_forever | 26 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/3_kill_it | 27 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/4_wait_for_it | 26 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/5_exited | 64 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/6_get | 29 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/7_get_output | 44 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/8_any | 43 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/qrsh_utils/9_alias | 38 | ||||
-rw-r--r-- | qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp | 52 | ||||
-rw-r--r-- | qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp | 50 | ||||
-rw-r--r-- | qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt | 309 |
17 files changed, 2325 insertions, 0 deletions
diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index 563068a018..01c58556d3 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -256,6 +256,18 @@ check_PROGRAMS+=datagen datagen_SOURCES=datagen.cpp datagen_LDADD=$(lib_common) +check_PROGRAMS+=qrsh_server +qrsh_server_SOURCES=qrsh_server.cpp +qrsh_server_LDADD=$(lib_client) + +check_PROGRAMS+=qrsh_run +qrsh_run_SOURCES=qrsh_run.cpp +qrsh_run_LDADD=$(lib_client) + +check_PROGRAMS+=qrsh +qrsh_SOURCES=qrsh.cpp +qrsh_LDADD=$(lib_client) + TESTS_ENVIRONMENT = \ VALGRIND=$(VALGRIND) \ diff --git a/qpid/cpp/src/tests/qrsh.cpp b/qpid/cpp/src/tests/qrsh.cpp new file mode 100644 index 0000000000..6b4240ce30 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh.cpp @@ -0,0 +1,166 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/AsyncSession.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageListener.h> +#include <qpid/client/SubscriptionManager.h> + + +#include <cstdlib> +#include <iostream> + +#include <sstream> + +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + + +class ResponseListener : public MessageListener +{ + public : + + int exitCode; + + ResponseListener ( SubscriptionManager & subscriptions ) + : exitCode(-1), + subscriptions ( subscriptions ) + { + } + + virtual void + received ( Message & message ) + { + char first_word[1000]; + sscanf ( message.getData().c_str(), "%s", first_word ); + + if ( ! strcmp ( first_word, "wait_response" ) ) + { + // If we receive a message here, parse out the exit code. + sscanf ( message.getData().c_str(), "%*s%d", & exitCode ); + subscriptions.cancel(message.getDestination()); + } + else + if ( ! strcmp ( first_word, "get_response" ) ) + { + // The remainder of the message is the file we requested. + fprintf ( stdout, + "%s", + message.getData().c_str() + strlen("get_response" ) + ); + subscriptions.cancel(message.getDestination()); + } + } + + + private : + + SubscriptionManager & subscriptions; +}; + + + + +/* + * argv[1] host + * argv[2] port + * argv[3] server name + * argv[4] command name + * argv[5..N] args to the command + */ +int +main ( int argc, char ** argv ) +{ + const char* host = argv[1]; + int port = atoi(argv[2]); + + + Connection connection; + + try + { + connection.open ( host, port ); + Session session = connection.newSession ( ); + + // Make a queue and bind it to fanout. + string myQueue = session.getId().getName(); + + session.queueDeclare ( arg::queue=myQueue, + arg::exclusive=true, + arg::autoDelete=true + ); + + session.exchangeBind ( arg::exchange="amq.fanout", + arg::queue=myQueue, + arg::bindingKey="my-key" + ); + + // Get ready to listen for the wait-response. + // or maybe a get-response. + // ( Although this may not be one of those types + // of command, get ready anyway. + SubscriptionManager subscriptions ( session ); + ResponseListener responseListener ( subscriptions ); + subscriptions.subscribe ( responseListener, myQueue ); + + bool response_command = false; + if(! strcmp("exec_wait", argv[4] )) + response_command = true; + else + if(! strcmp("exited", argv[4] )) + response_command = true; + else + if(! strcmp("get", argv[4] )) + response_command = true; + + // Send the payload message. + // Skip "qrsh host_name port" + Message message; + stringstream ss; + for ( int i = 3; i < argc; ++ i ) + ss << argv[i] << ' '; + + message.setData ( ss.str() ); + + session.messageTransfer(arg::content=message, + arg::destination="amq.fanout"); + + if ( response_command ) + subscriptions.run(); + + session.close(); + connection.close(); + return responseListener.exitCode; + } + catch ( exception const & e) + { + cerr << e.what() << endl; + } + + return 1; +} + + + diff --git a/qpid/cpp/src/tests/qrsh_run.cpp b/qpid/cpp/src/tests/qrsh_run.cpp new file mode 100644 index 0000000000..cfdd0cef80 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_run.cpp @@ -0,0 +1,321 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#include <iostream> +#include <sstream> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + + +using namespace std; + + + +int +main ( int argc, char ** argv ) +{ + int exit_code = -1; + int fd[2]; + int my_pid = getpid(); + int child_pid; + + pipe(fd); + + char const * root_dir = argv[1]; // This arg is prepended by qrsh_server. + char const * child_name = argv[2]; // This arg comes from qrsh. + char const * child_path = argv[3]; // This arg comes from qrsh. + + // This is the problem.. + fprintf ( stderr, "MDEBUG qrsh_run: root_dir: |%s|\n", root_dir ); + fprintf ( stderr, "MDEBUG qrsh_run: child_name: |%s|\n", child_name ); + fprintf ( stderr, "MDEBUG qrsh_run: child_path: |%s|\n", child_path ); + + /* + * A named child is one for whom we will create a directory and + * store information. There are some magic names that are not + * real symbolic names -- but are instead the names of actions. + */ + + bool named_child = true; + + if ( ! strcmp ( child_name, "exec" ) ) + named_child = false; + else + if ( ! strcmp ( child_name, "exec_wait" ) ) + named_child = false; + else + if ( ! strcmp ( child_name, "exited" ) ) + named_child = false; + else + named_child = true; + + stringstream child_dir_name; + + if ( named_child ) + { + child_dir_name << root_dir + << '/' + << child_name; + + /* + * Make the child directory before forking, or there is + * a race in which the child might be trying to make its + * stdout and stderr files while we are tring to make + * the directory. + */ + if ( -1 == mkdir ( child_dir_name.str().c_str(), 0777 ) ) + { + fprintf ( stderr, + "qrsh_run error: Can't mkdir |%s|\n", + child_dir_name.str().c_str() + ); + exit ( 1 ); + } + + } + else + /* + * If this is an 'exited' command that means we are + * waiting for a pre-existing child. + */ + if ( ! strcmp ( child_name, "exited" ) ) + { + int wait_pid = atoi(child_path); + + // Find the child's symbolic name. + stringstream pid_to_name_file_name; + pid_to_name_file_name << root_dir + << '/' + << wait_pid; + FILE * fp = fopen ( pid_to_name_file_name.str().c_str(), "r" ); + if (! fp) + { + fprintf ( stderr, + "qrsh_run %d error: Can't open pid2name file |%s|.\n", + my_pid, + pid_to_name_file_name.str().c_str() + ); + exit(1); + } + char symbolic_name[1000]; + strcpy ( symbolic_name, "qrsh_no_name" ); + fscanf ( fp, "%s", symbolic_name ); + fclose ( fp ); + + // Make the name of the child's exit code file. + stringstream exit_code_file_name; + exit_code_file_name << root_dir + << '/' + << symbolic_name + << "/exit_code"; + + struct stat stat_buf; + int file_does_not_exist = stat ( exit_code_file_name.str().c_str(), & stat_buf ); + + /* + * If the result of stat is zero, the file exists, which means that + * the command has exited. The question we are being asked here is + * "has it exited yet?" + */ + if ( ! file_does_not_exist ) + return 1; + else + if ( errno == ENOENT ) + return 0; + else + return 2 ; + } + + + // We are not waiting on a pre-wxiting child: we have a + // new child to create. + + child_pid = fork(); + + if ( child_pid == 0 ) + { + // This code is executed in the child process. + + // If it's a *named* child, then redirect its stdout and stderr. + if ( named_child ) + { + stringstream stdout_path, + stderr_path; + + // Redirect the child's stdout. ----------------- + stdout_path << root_dir + << '/' + << child_name + << '/' + << "stdout"; + + int redirected_stdout = open ( stdout_path.str().c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + S_IRWXU|S_IRWXG|S_IRWXO + ); + if ( redirected_stdout < 0 ) + { + perror ( "qrsh_run: error opening redirected_stdout: " ); + fprintf ( stderr, "stdout path: |%s|\n", stdout_path.str().c_str() ); + exit ( 1 ); + } + if ( -1 == dup2 ( redirected_stdout, 1 ) ) + { + perror ( "qrsh_run: dup2 (stdout) error: " ); + exit(1); + } + + // Redirect the child's stderr. ----------------- + stderr_path << root_dir + << '/' + << child_name + << '/' + << "stderr"; + + int redirected_stderr = open ( stderr_path.str().c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + S_IRWXU|S_IRWXG|S_IRWXO + ); + if ( redirected_stderr < 0 ) + { + perror ( "qrsh_run: error opening redirected_stderr: " ); + fprintf ( stderr, "stderr path: |%s|\n", stderr_path.str().c_str() ); + exit ( 1 ); + } + if(-1 == dup2 ( redirected_stderr, 2 ) ) + { + perror ( "qrsh_run: dup2 (stderr) error: " ); + exit(1); + } + } + + fprintf ( stderr, "MDEBUG ------------- qrsh_run argv -------------\n" ); + for ( int i = 0; i < argc; ++ i ) + fprintf ( stderr, "MDEBUG argv[%d] : |%s|\n", i, argv[i] ); + + execv ( child_path, argv + 2 ); + perror ( "qrsh_run: execv error: " ); + fprintf ( stderr, "on path |%s|\n", child_path ); + exit ( 1 ); + } + else + { + // This code is executed in the parent process. + + if ( named_child ) + { + // Write the name-to-pid mapping. + stringstream pid_file_name; + pid_file_name << child_dir_name.str() + << "/pid"; + + FILE * fp; + if ( ! (fp = fopen ( pid_file_name.str().c_str(), "w") ) ) + { + fprintf ( stderr, + "qrsh_run %d error: Can't open file |%s|\n", + my_pid, + pid_file_name.str().c_str() + ); + exit(1); + } + fprintf ( fp, "%d\n", child_pid ); + fclose ( fp ); + + + // Write the pid-to-name mapping. + stringstream name_to_pid_file_name; + name_to_pid_file_name << root_dir + << '/' + << child_pid; + if(! (fp = fopen ( name_to_pid_file_name.str().c_str(), "w"))) + { + fprintf ( stderr, + "qrsh_run %d error: Can't open file |%s|\n", + my_pid, + name_to_pid_file_name.str().c_str() + ); + exit(1); + } + fprintf ( fp, "%s\n", child_name ); + fclose(fp); + } + + pid_t awaited_pid; + while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) ) + { + fprintf ( stderr, + "qrsh_run %d info: parent: waiting for child %d...\n", + my_pid, + child_pid + ); + sleep(1); + } + + if ( -1 == awaited_pid ) + { + fprintf ( stderr, "qrsh_run error awaiting child!\n" ); + exit ( 1 ); + } + + /* + * Write the exit code. + */ + exit_code >>= 8; + + if ( named_child ) + { + if ( child_pid == awaited_pid ) + { + stringstream exit_code_file_name; + exit_code_file_name << child_dir_name.str() + << "/exit_code"; + + FILE * fp; + if ( ! (fp = fopen ( exit_code_file_name.str().c_str(), "w") ) ) + { + fprintf ( stderr, + "qrsh_run error: Can't open file |%s|\n", + exit_code_file_name.str().c_str() + ); + exit(1); + } + fprintf ( fp, "%d\n", exit_code ); + fclose ( fp ); + } + } + } + + fprintf ( stderr, "MDEBUG qrsh_run returning exit code %d\n", exit_code ); + return exit_code; +} + + + + diff --git a/qpid/cpp/src/tests/qrsh_server.cpp b/qpid/cpp/src/tests/qrsh_server.cpp new file mode 100644 index 0000000000..ab715801c9 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_server.cpp @@ -0,0 +1,1062 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + + +#include <unistd.h> +#include <cstdlib> +#include <iostream> +#include <map> +#include <dirent.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/AsyncSession.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageListener.h> +#include <qpid/client/SubscriptionManager.h> + + +using namespace qpid::client; +using namespace qpid::framing; +using namespace std; + + + + +int +mrand ( int max_desired_val ) +{ + double zero_to_one = (double) rand() / (double) RAND_MAX; + return (int) (zero_to_one * (double) max_desired_val); +} + + + +char * +file2str ( char const * file_name ) +{ + FILE * fp = fopen ( file_name, "r" ); + if(! fp) + { + fprintf ( stderr, "file2str error: can't open file |%s|.\n", file_name ); + return 0; + } + + fseek ( fp, 0, SEEK_END ); + size_t file_len = (size_t) ftell ( fp ); + rewind ( fp ); + char * content = (char *) malloc ( file_len + 1 ); + + if ( ! content ) + { + fprintf ( stderr, + "file2str error: can't malloc %d bytes.\n", + (int)file_len + ); + return 0; + } + + size_t items_read = fread ( content, file_len, 1, fp ); + + if ( 1 != items_read ) + { + fprintf ( stderr, "file2str error: read failed.\n" ); + free ( content ); + return 0; + } + + fclose ( fp ); + content[file_len] = 0; + + return content; +} + + + + + +class QrshServer : public MessageListener +{ + public: + + QrshServer ( SubscriptionManager & subscriptions, + char const * name, + char const * qrsh_run_path, + char const * host, + int port + ); + + virtual void received ( Message & message); + + + private: + + set<string> all_server_names; + + stringstream data_dir; + + SubscriptionManager & subscriptions; + + // Is this message addressed to me? + bool myMessage ( Message const & message ); + + /* ---------------------------------------------- + * Special Commands + * These are commands that the qrsh_server executes + * directly, rather than through a child process + * instance of qrsh_run. + */ + void runCommand ( Message const & message ); + void execute ( Message const & message ); + void wait ( Message const & message ); + void exited ( Message const & message ); + void get ( Message const & message ); + void rememberIntroduction ( Message const & message ); + void getStraw ( Message const & message ); + void addAlias ( Message const & message ); + + void start ( ); + void sayHello ( ); + void sayName ( ); + // end Special Commands ------------------------ + + + void saveCommand ( Message const & message ); + + void send ( string const & content ); + + void drawStraws ( ); + void getNames ( ); + void runSavedCommand ( ); + + char ** getArgs ( char const * s ); + bool isProcessName ( char const * s ); + int string_countWords ( char const * s ); + char const * skipWord ( char const * s ); + + + void string_replaceAll ( string & str, + string & target, + string & replacement + ); + + + string name, + qrsh_run_path, + host; + + vector<string *> aliases; + + int port; + + map < char *, int > abstract_name_map; + + set < string > myFellowBrokers; + + bool saidHello; + + Message savedCommand; + + vector < int > straws; + int myStraw; + +}; + + + +QrshServer::QrshServer ( SubscriptionManager & subs, + char const * name, + char const * qrsh_run_path, + char const * host, + int port + ) + : subscriptions ( subs ), + name ( name ), + qrsh_run_path ( qrsh_run_path ), + host ( host ), + port ( port ), + saidHello ( false ), + myStraw ( 0 ) +{ + data_dir << "/tmp/qrsh_" + << getpid(); + + if(mkdir ( data_dir.str().c_str(), 0777 ) ) + { + fprintf ( stderr, + "QrshServer::QrshServer error: can't mkdir |%s|\n", + data_dir.str().c_str() + ); + exit ( 1 ); + } +} + + + +void +QrshServer::saveCommand ( Message const & message ) +{ + savedCommand = message; +} + + + +void +QrshServer::runSavedCommand ( ) +{ + runCommand ( savedCommand ); +} + + + +void +QrshServer::start ( ) +{ + stringstream announcement_data; + announcement_data << "hello_my_name_is " + << name; + + send ( announcement_data.str() ); + + saidHello = true; +} + + + + +void +QrshServer::send ( string const & content ) +{ + try + { + Message message; + message.setData ( content ); + + Connection connection; + connection.open ( host, port ); + Session session = connection.newSession ( ); + session.messageTransfer ( arg::content = message, + arg::destination = "amq.fanout" + ); + session.close(); + connection.close(); + } + catch ( exception const & e ) + { + fprintf ( stderr, "QrshServer::send error: |%s|\n", e.what() ); + } +} + + + + +void +QrshServer::sayHello ( ) +{ + if ( saidHello ) + return; + + stringstream ss; + + ss << "hello_my_name_is " + << name; + + send ( ss.str() ); + saidHello = true; +} + + + +void +QrshServer::sayName ( ) +{ + fprintf ( stderr, "My name is: |%s|\n", name.c_str() ); +} + + + + +void +QrshServer::drawStraws ( ) +{ + myStraw = mrand ( 1000000000 ); + stringstream ss; + ss << "straw " + << name + << ' ' + << myStraw; + send ( ss.str() ); +} + + + +void +QrshServer::getStraw ( Message const & message ) +{ + int straw; + + char brokerName[1000]; + sscanf ( message.getData().c_str(), "%*s%s", brokerName ); + + if ( ! strcmp ( brokerName, name.c_str() ) ) + return; + + sscanf ( message.getData().c_str(), "%*s%*s%d", & straw ); + straws.push_back ( straw ); + + bool i_win = true; + int ties = 0; + + if ( straws.size() >= myFellowBrokers.size() ) + { + // All votes are in! Let's see if I win! + for ( unsigned int i = 0; i < straws.size(); ++ i ) + { + if ( straws[i] == myStraw ) + ++ ties; + else + if ( straws[i] > myStraw ) + { + i_win = false; + break; + } + } + + if ( i_win && (ties <= 0) ) + { + myStraw = 0; + straws.clear(); + runSavedCommand ( ); + } + else + if ( i_win && (ties > 0) ) + { + fprintf ( stderr, "MDEBUG oh no! drawStraws error: server %s tied with straw %d!\n", name.c_str(), straw ); + } + } +} + + + + +/* + * "APB" command (all-points-bullitens (commands that are not addressed + * specifically to any server)) are handled directly, here. + * Because if I return simply "true", the normal command processing code + * will misinterpret the command. + */ +bool +QrshServer::myMessage ( Message const & message ) +{ + int const maxlen = 100; + char head[maxlen]; + char first_word [ maxlen + 1 ]; + strncpy ( head, message.getData().c_str(), maxlen ); + sscanf ( head, "%s", first_word ); + + if ( ! strcmp ( name.c_str(), first_word ) ) + { + return true; + } + else + { + // Is the given name one of my aliases? + char possibleAlias[1000]; + if(1 == sscanf ( message.getData().c_str(), "%s", possibleAlias )) + { + for ( unsigned int i = 0; i < aliases.size(); ++ i ) + { + + if ( ! strcmp ( possibleAlias, aliases[i]->c_str() )) + { + return true; + } + } + } + } + + if ( ! strcmp ( first_word, "hello_my_name_is" ) ) + { + rememberIntroduction ( message ); + sayHello ( ); + return false; + } + else + if ( ! strcmp ( first_word, "straw" ) ) + { + getStraw ( message ); + return false; + } + else + if ( ! strcmp ( first_word, "all" ) ) + { + return true; + } + else + if ( ! strcmp ( first_word, "any" ) ) + { + straws.clear(); + usleep ( 200000 ); + saveCommand ( message ); + drawStraws ( ); + return false; + } + else + return false; +} + + + + +void +QrshServer::rememberIntroduction ( Message const & message ) +{ + char brokerName [ 1000 ]; + sscanf ( message.getData().c_str(), "%*s%s", brokerName ); + + if ( strcmp ( brokerName, name.c_str() ) ) + myFellowBrokers.insert ( string ( brokerName ) ); +} + + + + +void +QrshServer::addAlias ( Message const & message ) +{ + char alias[1000]; + sscanf ( message.getData().c_str(), "%*s%*s%s", alias ); + aliases.push_back ( new string(alias) ); +} + + + + +void +QrshServer::getNames ( ) +{ + abstract_name_map.clear(); + + DIR * dir = opendir ( data_dir.str().c_str() ); + + if ( ! dir ) + { + fprintf ( stderr, + "QrshServer::getNames error: could not open dir |%s|.\n", + data_dir.str().c_str() + ); + return; + } + + struct dirent * file; + while ( (file = readdir ( dir ) ) ) + { + if ( '.' != file->d_name[0] ) + { + stringstream pid_file_name; + pid_file_name << data_dir.str() + << '/' + << file->d_name + << "/pid"; + + int pid = 0; + FILE * fp; + if ( (fp = fopen ( pid_file_name.str().c_str(), "r" ) ) ) + { + fscanf ( fp, "%d", & pid ); + fclose ( fp ); + abstract_name_map.insert(pair<char*, int>(strdup(file->d_name), pid)); + } + else + { + /* + * Fail silently. The non-existence of this file + * is not necessarily an error. + */ + } + } + } + closedir ( dir ); +} + + + +void +QrshServer::string_replaceAll ( string & str, + string & target, + string & replacement + ) +{ + int target_size = target.size(); + int found_pos = 0; + + while ( 0 <= (found_pos = str.find ( target ) ) ) + str.replace ( found_pos, target_size, replacement ); +} + + + + +bool +QrshServer::isProcessName ( char const * str ) +{ + getNames(); + map<char *, int>::iterator it; + for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) + { + if ( ! strcmp ( str, it->first ) ) + return true; + } + + return false; +} + + + + + +int +QrshServer::string_countWords ( char const * s1 ) +{ + int count = 0; + char const * s2 = s1 + 1; + + if ( ! isspace(* s1) ) + { + ++ count; + } + + for ( ; * s2; ++ s1, ++ s2 ) + { + // count space-to-word transitions. + if ( isspace(*s1) && (! isspace(*s2)) ) + ++ count; + } + + return count; +} + + + + +void +QrshServer::execute ( Message const & message ) +{ + // First, gather all the symbolic names we know. + getNames(); + + // Now make a copy of the command, that I can alter. + string command ( message.getData() ); + + + // Replace each occurrence of every abstract name with its pid. + char pid_str[100]; + map<char *, int>::iterator it; + for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) + { + sprintf ( pid_str, "%d", it->second ); + string target ( it->first ), + replacement ( pid_str ); + string_replaceAll ( command, target, replacement ); + } + + + char const * truncated_command = skipWord(skipWord(command.c_str())); + + if ( truncated_command ) + system ( truncated_command ); +} + + + + + +void +QrshServer::get ( Message const & request_message ) +{ + char * file_content; + + /* + * Get the contents of the requested file. + */ + char file_or_process_name[1000]; + sscanf ( request_message.getData().c_str(), "%*s%*s%s", file_or_process_name ); + + if ( isProcessName ( file_or_process_name ) ) + { + stringstream desired_file_name; + desired_file_name << data_dir.str() + << '/' + << file_or_process_name + << '/'; + char requested_output_stream[1000]; + if(1 != sscanf ( request_message.getData().c_str(), + "%*s%*s%*s%s", + requested_output_stream + ) + ) + { + fprintf ( stderr, + "QrshServer::get error: Can't read requested data file name from this message: |%s|\n", + request_message.getData().c_str() + ); + return; + } + desired_file_name << requested_output_stream; + file_content = file2str ( desired_file_name.str().c_str() ); + } + else + { + file_content = file2str ( file_or_process_name ); + } + + stringstream reply_data ; + reply_data << "get_response " + << file_content; + /* + * Send a response-message to the server who is waiting. + */ + send ( reply_data.str() ); +} + + + + + + +void +QrshServer::exited ( Message const & message ) +{ + int exit_code = -1; + + // First, gather all the symbolic names we know. + getNames(); + + // Now make a copy of the command, that I can alter. + string edited_command ( message.getData() ); + + // Replace each occurrence of every abstract name with its pid. + char pid_str[100]; + map<char *, int>::iterator it; + for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) + { + sprintf ( pid_str, "%d", it->second ); + string target ( it->first ), + replacement ( pid_str ); + string_replaceAll ( edited_command, target, replacement ); + } + + // Skip the service name. That is not used by the child. + char const * truncated_command = skipWord(edited_command.c_str()); + + if ( truncated_command ) + { + stringstream ss; + ss << qrsh_run_path + << ' ' + << data_dir.str() + << ' ' + << truncated_command; + + int child_pid; + if ( ! (child_pid = fork() ) ) + { + // This is the child. + + char ** argv = getArgs ( ss.str().c_str() ); + execv ( qrsh_run_path.c_str(), argv ); + + perror ( "qrsh_server: execv error: " ); + exit ( 1 ); + } + else + { + // This is the parent. + pid_t awaited_pid; + while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) ) + { + fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" ); + sleep(1); + } + + if ( -1 == awaited_pid ) + { + fprintf ( stderr, "qrsh_server error awaiting child!\n" ); + exit ( 1 ); + } + + exit_code >>= 8; + + stringstream data; + data << "wait_response " + << exit_code; + + send ( data.str() ); + } + } +} + + + + +void +QrshServer::wait ( Message const & message ) +{ + bool pre_existing = false; + if ( 3 == string_countWords ( message.getData().c_str() ) ) + { + // The first word is the name of this service. + // The second word is "exec_wait". + // The third word is the symbolic name of the command to wait for. + // The fact that there are exactly three words means that this + // must be a command that has already been named and started -- + // we just need to find its pid and wait on it. + pre_existing = true; + } + + + int exit_code = -1; + + // First, gather all the symbolic names we know. + getNames(); + + // Now make a copy of the command, that I can alter. + string edited_command ( message.getData() ); + + // Replace each occurrence of every abstract name with its pid. + char pid_str[100]; + map<char *, int>::iterator it; + for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) + { + sprintf ( pid_str, "%d", it->second ); + string target ( it->first ), + replacement ( pid_str ); + string_replaceAll ( edited_command, target, replacement ); + } + + // Skip the service name. That is not used by the child. + char const * truncated_command = skipWord(edited_command.c_str()); + + if ( truncated_command ) + { + stringstream ss; + ss << qrsh_run_path + << ' ' + << data_dir.str() + << ' ' + << truncated_command; + + int child_pid; + if ( ! (child_pid = fork() ) ) + { + // This is the child. + + char ** argv = getArgs ( ss.str().c_str() ); + execv ( qrsh_run_path.c_str(), argv ); + + perror ( "qrsh_server: execv error: " ); + exit ( 1 ); + } + else + { + // This is the parent. + pid_t awaited_pid; + while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) ) + { + fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" ); + sleep(1); + } + + if ( -1 == awaited_pid ) + { + fprintf ( stderr, "qrsh_server error awaiting child!\n" ); + exit ( 1 ); + } + } + + exit_code >>= 8; + + stringstream data; + data << "wait_response " + << exit_code; + + send ( data.str() ); + } +} + + + + + +char const * +QrshServer::skipWord ( char const * s ) +{ + if(! (s && *s) ) + return 0; + + // skip past initial white space + while ( isspace(*s) ) + { + ++ s; + if(! *s) + return 0; + } + + // skip past first word + while ( ! isspace(*s) ) + { + ++ s; + if(! *s) + return 0; + } + + return s; +} + + + + + +char ** +QrshServer::getArgs ( char const * str ) +{ + char const * s = str; + + char ** argv = 0; + vector<int> start_positions, + lengths; + + int pos = 0; + int arg_len = 0; + + int n_args = 0; + while ( 1 ) + { + // advance over whitespace. + while ( isspace ( *s ) ) + { + ++ s; ++ pos; + if(! *s) + { + goto done; + } + } + + ++ n_args; + start_positions.push_back ( pos ); + arg_len = 0; + + // advance over non-whitespace. + while ( ! isspace ( *s ) ) + { + ++ s; ++ pos; ++ arg_len; + if(! *s) + { + lengths.push_back ( arg_len ); + arg_len = 0; + goto done; + } + } + + lengths.push_back ( arg_len ); + arg_len = 0; + } + + done: + + if ( arg_len > 0 ) + lengths.push_back ( arg_len ); + + // Alloc the array. + argv = (char **) malloc ( sizeof(char *) * ( n_args + 1 ) ); + argv[n_args] = 0; // mull-term the array. + + for ( int i = 0; i < n_args; ++ i ) + { + argv[i] = ( char *) malloc ( lengths[i] + 1 ); + strncpy ( argv[i], + str + start_positions[i], + lengths[i] + ); + argv[i][lengths[i]] = 0; + } + + return argv; +} + + + +void +QrshServer::runCommand ( Message const & message ) +{ + char const * s = message.getData().c_str(); + + /* + * Skip the first word, which is this server's name. + */ + while ( isspace(*s) ) // go to start of first word. + ++ s; + + while ( ! isspace(*s) ) // go to end of first word. + ++ s; + + while ( isspace(*s) ) // go to start of second word. + ++ s; + + char command_name[1000]; + sscanf ( s, "%s", command_name ); + + if ( ! strcmp ( "get", command_name ) ) + { + get ( message ); + } + else + if ( ! strcmp ( "exited", command_name ) ) + { + exited ( message ); + } + else + if ( ! strcmp ( "exec_wait", command_name ) ) + { + wait ( message ); + } + else + if ( ! strcmp ( "exec", command_name ) ) + { + execute ( message ); + } + else + if ( ! strcmp ( "start", command_name ) ) + { + start ( ); + } + else + if ( ! strcmp ( "alias", command_name ) ) + { + addAlias ( message ); + } + else + if ( ! strcmp ( "sayName", command_name ) ) + { + sayName ( ); + } + else + { + /* + * If the command is not any of the "special" commands + * above, then it's a "normal" command. + * That means we run it with a child process instance of + * qrsh_run, which will save all its data in the qrsh dir. + */ + stringstream ss; + ss << qrsh_run_path + << ' ' + << data_dir.str() + << ' ' + << s; + + if ( ! fork() ) + { + char ** argv = getArgs ( ss.str().c_str() ); + execv ( qrsh_run_path.c_str(), argv ); + perror ( "qrsh_server: execv error: " ); + } + } +} + + + +void +QrshServer::received ( Message & message ) +{ + if ( myMessage ( message ) ) + runCommand ( message ); +} + + + + + +/* + * fixme mick Mon Aug 3 10:29:26 EDT 2009 + * argv[1] server name + * argv[2] qrsh exe path + * argv[3] host + * argv[4] port + */ +int +main ( int /*argc*/, char** argv ) +{ + const char* host = argv[3]; + int port = atoi(argv[4]); + Connection connection; + Message msg; + + srand ( getpid() ); + + try + { + connection.open ( host, port ); + Session session = connection.newSession(); + + + // Declare queues. + string myQueue = session.getId().getName(); + session.queueDeclare ( arg::queue=myQueue, + arg::exclusive=true, + arg::autoDelete=true); + + session.exchangeBind ( arg::exchange="amq.fanout", + arg::queue=myQueue, + arg::bindingKey="my-key"); + + // Create a server and subscribe it to my queue. + SubscriptionManager subscriptions ( session ); + QrshServer server ( subscriptions, + argv[1], // server name + argv[2], // qrsh exe path + host, + port + ); + subscriptions.subscribe ( server, myQueue ); + + // Receive messages until the subscription is cancelled + // by QrshServer::received() + subscriptions.run(); + + connection.close(); + } + catch(const exception& error) + { + cout << error.what() << endl; + return 1; + } + + return 0; +} + + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/10_all b/qpid/cpp/src/tests/qrsh_utils/10_all new file mode 100755 index 0000000000..7b486ea672 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/10_all @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +echo "Asking all servers to say their names... " +qrsh 127.0.0.1 5813 \ + all sayName + + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/1_remote_run b/qpid/cpp/src/tests/qrsh_utils/1_remote_run new file mode 100755 index 0000000000..5b9b307bba --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/1_remote_run @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + + +./qrsh 127.0.0.1 5813 \ + mrg23 command_1 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz diff --git a/qpid/cpp/src/tests/qrsh_utils/2_forever b/qpid/cpp/src/tests/qrsh_utils/2_forever new file mode 100755 index 0000000000..5528b0e4d8 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/2_forever @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + + +./qrsh 127.0.0.1 5813 \ + mrg23 command_2 /home/mick/redhat/qrsh/qrsh_run/forever foo bar baz diff --git a/qpid/cpp/src/tests/qrsh_utils/3_kill_it b/qpid/cpp/src/tests/qrsh_utils/3_kill_it new file mode 100755 index 0000000000..afc7a03c9d --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/3_kill_it @@ -0,0 +1,27 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +echo "Killing command 2... " +./qrsh 127.0.0.1 5813 \ + mrg23 exec kill -9 command_2 + diff --git a/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it b/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it new file mode 100755 index 0000000000..a4dc0da1ce --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +./qrsh 127.0.0.1 5813 \ + mrg23 exec_wait /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz +echo "my_command returned an exit code of $?" diff --git a/qpid/cpp/src/tests/qrsh_utils/5_exited b/qpid/cpp/src/tests/qrsh_utils/5_exited new file mode 100755 index 0000000000..4fec1dcc79 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/5_exited @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +path=/home/mick/redhat/qrsh/qrsh_run + +echo "Running command_3 ..." +./qrsh 127.0.0.1 5813 \ + mrg23 command_3 $path/my_command foo bar baz + +echo "Now I do some other stuff..." +sleep 1 +echo "And then some more stuff..." +sleep 1 +echo "and so on..." +sleep 1 + +echo "Now I'm waiting for command_3 ..." +./qrsh 127.0.0.1 5813 \ + mrg23 exited command_3 +echo "has command_3 exited: $? ." +sleep 5 + +./qrsh 127.0.0.1 5813 \ + mrg23 exited command_3 +echo "has command_3 exited: $? ." +sleep 5 + +./qrsh 127.0.0.1 5813 \ + mrg23 exited command_3 +echo "has command_3 exited: $? ." +sleep 5 + +./qrsh 127.0.0.1 5813 \ + mrg23 exited command_3 +echo "has command_3 exited: $? ." +sleep 5 + +./qrsh 127.0.0.1 5813 \ + mrg23 exited command_3 +echo "has command_3 exited: $? ." +sleep 5 + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/6_get b/qpid/cpp/src/tests/qrsh_utils/6_get new file mode 100755 index 0000000000..4b35ca98e6 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/6_get @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +echo "getting /tmp/foo ..." +./qrsh 127.0.0.1 5813 \ + mrg23 get /tmp/foo + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/7_get_output b/qpid/cpp/src/tests/qrsh_utils/7_get_output new file mode 100755 index 0000000000..59911089ec --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/7_get_output @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +echo "Run a command..." +./qrsh 127.0.0.1 5813 \ + mrg23 command_4 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz + +echo "Wait for a while..." +sleep 20 + +echo "Get stderr output:" +echo "------------- begin stderr ---------------" +./qrsh 127.0.0.1 5813 \ + mrg23 get command_4 stderr +echo "------------- end stderr ---------------" +echo " " +echo " " +echo " " +echo "Get stdout output:" +echo "------------- begin stdout ---------------" +./qrsh 127.0.0.1 5813 \ + mrg23 get command_4 stdout +echo "------------- end stdout ---------------" + diff --git a/qpid/cpp/src/tests/qrsh_utils/8_any b/qpid/cpp/src/tests/qrsh_utils/8_any new file mode 100755 index 0000000000..2a922ea0e0 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/8_any @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +echo "asking any server to say his name ..." +./qrsh 127.0.0.1 5813 \ + any sayName +sleep 1 +echo "asking any server to say his name ..." +./qrsh 127.0.0.1 5813 \ + any sayName +sleep 1 +echo "asking any server to say his name ..." +./qrsh 127.0.0.1 5813 \ + any sayName +sleep 1 +echo "asking any server to say his name ..." +./qrsh 127.0.0.1 5813 \ + any sayName +sleep 1 + + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/9_alias b/qpid/cpp/src/tests/qrsh_utils/9_alias new file mode 100755 index 0000000000..a4cfdfdf9a --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/9_alias @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#! /bin/bash + +# Make a group of two of the servers, using "alias", +# and send the group a command. + +qrsh 127.0.0.1 5813 \ + mrg22 alias group_1 +qrsh 127.0.0.1 5813 \ + mrg23 alias group_1 + +echo "Asking group_1 to say their names... " +qrsh 127.0.0.1 5813 \ + group_1 sayName + + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp b/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp new file mode 100644 index 0000000000..386e2f73f0 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +#include <stdio.h> +#include <unistd.h> + + + +main ( int argc, char ** argv ) +{ + fprintf ( stderr, "Hello, I am the Example Child!\n"); + fprintf ( stderr, "my arguments %d are:\n", argc - 1 ); + fprintf ( stdout, "And hello to stdout, too!\n"); + + int i; + for ( i = 1; i < argc; ++ i ) + { + fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] ); + } + + for ( i = 0; i < 15; ++ i ) + { + fprintf ( stderr, "child sleeping...\n" ); + sleep ( 1 ); + } + + fprintf ( stderr, "child exiting with code 13.\n" ); + + return 13; +} + + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp b/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp new file mode 100644 index 0000000000..191a9bca11 --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + + +#include <stdio.h> +#include <unistd.h> + + + +main ( int argc, char ** argv ) +{ + fprintf ( stderr, "Hello, I am the Forever Example Child!\n"); + fprintf ( stderr, "my %d arguments are:\n", argc - 1 ); + + int i; + for ( i = 1; i < argc; ++ i ) + fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] ); + + for ( i = 0; i >= 0; ++ i ) + { + fprintf ( stderr, "child sleeping forever %d ...\n" , i); + sleep ( 1 ); + } + + fprintf ( stderr, "child exiting with code 12.\n" ); + + return 12; +} + + + + diff --git a/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt b/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt new file mode 100644 index 0000000000..ad5990b38b --- /dev/null +++ b/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt @@ -0,0 +1,309 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ + +############################################## + qrsh: a Qpid-based remote shell utility + + Last updated: 3 Aug 09 Mick Goulish +############################################## + + + +============================= +Overview +============================= + + You're writing a multi-box test, and you want to write a + shell script in which you start processes on other boxes + and kill them (or send arbitrary signals to them). + + But ssh doesn't let you signal them, and bash isn't the + greatest language in the world for creating data structures + (like you need to associate the PIDs with box names and + executable names.) + + Qsh is a utility implemented on Qpid that you can use from + within your bash script, or any other scripting language. + With it, you can: + + 1. run any executable on any box in your cluster. + + 2. don't worry about PIDs and box-names. You associate + your own abstract names with the executable instances, + and then use those names in the rest of your script. + I.e. "broker_1" "sender_3" etc. + + 3. Launch the executable and wait until it returns, and + get its exit code. + + 4. Launch your executable and do other stuff, then come + back later and see if it has exited. + + 5. Get whatever it sent to stdout or stderr. + + 6. Get the contents of any other file. + + 7. send a command to all your boxes at once + + 8. send a command to a randomly selected box. + + 9. define groups of boxes, and send a command simultaneously + to all boxes in a given group. + + + + +============================= +Using It +============================= + + 1. You need to run a Qpid broker. + + 2. You start a Qpid client ( which is called a qrsh_server ) + on all the boxes you care about. And you give them all + names like "mrg13", "mrg14" etc. The names can be anything + you want, but I've always used one qrsh_server per box, + and given it the box name. ( However, you can run two on + one box, they won't collide. ) + + 3. After you start all servers, send a "start" command to any + one of them: + + 4. The qrsh_servers use the fanout exchange to talk to each + other. + + 5. In your script, you run an executable called "qrsh". It knows + how to talk to the servers, do what you want, and retrieve + the data you want. + + + example start script: (this does 4 servers on the same box) + ------------------------------------------------------------- + + echo "Starting server mrg22 ..." + ./qrsh_server mrg22 ./qrsh_run 127.0.0.1 5813 & + + echo "Starting server mrg23 ..." + ./qrsh_server mrg23 ./qrsh_run 127.0.0.1 5813 & + + echo "Starting server mrg24 ..." + ./qrsh_server mrg24 ./qrsh_run 127.0.0.1 5813 & + + echo "Starting server mrg25 ..." + ./qrsh_server mrg25 ./qrsh_run 127.0.0.1 5813 & + + echo "Issuing start command..." + sleep 2 + ./qrsh 127.0.0.1 5813 mrg22 start + sleep 1 + + echo "Ready." + + # end of script. + + + + + + +============================= +Qrsh Syntax +============================= + + qrsh host port server_name command_name arg* + + + "host" and "port" specify the Qpid server to connect to. + + "server_name" can be anything you want. I always use the name + of the box that the server is running on. + + "command_name" is the name that you choose to assign to + the process you are running. Each process that you decide + to name must have a unique name within this script. + + Or it could be a reserved command name, that Qsh + interprets in a special way. + + Reserved command names are: + + exec + exec_wait + exited + get + + "exec" means "interpret the rest of the command line as a + command to be executed by the designated server. + + "exec_wait" means same as "exec", but wait for the command + to terminate, and return its exit code. + + "exited" -- you provide 1 arg, which is an abstract + process name. qrsh returns 1 if that process has exited, + else 0. + + "get" -- you provide one arg which is a path. qrsh returns + (by printing to stdout) the contents of that file. + + "arg*" is zero or more arguments. They are interpreted + differently depending on whether you are using one of + the above reserved command names, or making up your own + abstract name for a command. + + + + +============================= +Examples +============================= + + 1. Run a process on a remote box. + + qrsh mrg23 command_1 /usr/sbin/whatever foo bar baz + + Returns immediately. + + + + 2. Kill a process that you started earlier: + + qrsh mrg23 exec kill -9 command_1 + + After the word "exec" put any command line you want. + The server you're sending this to will replace all abstract + names in the command with process IDs. ( In this example, + just the word "command_1" will be replaced. ) Then it will + execute the command. + + + + 3. Execute a command, and wait for it to finish + + qrsh mrg23 exec_wait command_name args + + + + 4. Check on whether a command you issude earlier has exited. + + ./qrsh mrg23 exited command_3 + + Returns 1 if it has exited, else 0. + + + + 5. Get the contents of a file from the remote system: + + ./qrsh mrg23 get /tmp/foo + + Prints the contents to stdout. + + + + 6. Send a command to all servers at once: + + # This example causes them all to print thir names to stderr. + ./qrsh all sayName + + + 7. Define a group of servers and send a command to that group. + + #! /bin/bash + + # Make a group of two of the servers, using "alias", + # and send the group a command. + + qrsh 127.0.0.1 5813 \ + mrg22 alias group_1 + + qrsh 127.0.0.1 5813 \ + mrg23 alias group_1 + + echo "Asking group_1 to say their names... " + qrsh 127.0.0.1 5813 \ + group_1 sayName + + # end of script. + + + + + 8. Execute a command and get its stdout and stderr contents. + + #! /bin/bash + + echo "Run a command..." + ./qrsh 127.0.0.1 5813 \ + mrg23 command_4 my_command foo bar baz + + echo "Wait for a while..." + sleep 10 + + echo "Get stderr output:" + echo "------------- begin stderr ---------------" + ./qrsh 127.0.0.1 5813 \ + mrg23 get command_4 stderr + echo "------------- end stderr ---------------" + echo " " + + echo " " + echo "Get stdout output:" + echo "------------- begin stdout ---------------" + ./qrsh 127.0.0.1 5813 \ + mrg23 get command_4 stdout + echo "------------- end stdout ---------------" + + # end of script. + + + + + 9. Send a command to one of your servers, selected + at random. + + #! /bin/bash + + # I do it multiple times here, so I can see + # that it really is selecting randomly. + + echo "asking any server to say his name ..." + ./qrsh 127.0.0.1 5813 \ + any sayName + sleep 1 + + echo "asking any server to say his name ..." + ./qrsh 127.0.0.1 5813 \ + any sayName + sleep 1 + + echo "asking any server to say his name ..." + ./qrsh 127.0.0.1 5813 \ + any sayName + sleep 1 + + echo "asking any server to say his name ..." + ./qrsh 127.0.0.1 5813 \ + any sayName + + # end of script. + + + + |