Issue #2257 - edexBridge now uses qpid 0.18 (64-bit)

- Amend: updated for 32-bit
- Amend: close sender, session, and connection independently

Change-Id: I9b8cafbc10d81782a812e9d2d57930e859fa29a5

Former-commit-id: e8f8d498cd15855b72c170f2990fd4929eb636b2
This commit is contained in:
Bryan Kowal 2013-08-15 10:35:30 -05:00
parent 727eba03b9
commit da29ff63f4
6 changed files with 562 additions and 548 deletions

View file

@ -26,7 +26,7 @@
<option id="gnu.cpp.compiler.exe.release.option.optimization.level.208885184" name="Optimization Level" superClass="gnu.cpp.compiler.exe.release.option.optimization.level" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/> <option id="gnu.cpp.compiler.exe.release.option.optimization.level.208885184" name="Optimization Level" superClass="gnu.cpp.compiler.exe.release.option.optimization.level" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
<option id="gnu.cpp.compiler.exe.release.option.debugging.level.1161967936" name="Debug Level" superClass="gnu.cpp.compiler.exe.release.option.debugging.level" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/> <option id="gnu.cpp.compiler.exe.release.option.debugging.level.1161967936" name="Debug Level" superClass="gnu.cpp.compiler.exe.release.option.debugging.level" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
<option id="gnu.cpp.compiler.option.include.paths.1128011603" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath"> <option id="gnu.cpp.compiler.option.include.paths.1128011603" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/qpid/include}&quot;"/> <listOptionValue builtIn="false" value="/awips2/qpid/include"/>
<listOptionValue builtIn="false" value="/usr/local/ldm-6.8.1/src/config"/> <listOptionValue builtIn="false" value="/usr/local/ldm-6.8.1/src/config"/>
<listOptionValue builtIn="false" value="/usr/local/ldm-6.8.1/src/protocol"/> <listOptionValue builtIn="false" value="/usr/local/ldm-6.8.1/src/protocol"/>
<listOptionValue builtIn="false" value="/usr/local/ldm-6.8.1/src/ulog"/> <listOptionValue builtIn="false" value="/usr/local/ldm-6.8.1/src/ulog"/>
@ -84,6 +84,11 @@
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/> <storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/> <storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="edexBridge.cdt.managedbuild.target.gnu.exe.32670172" name="Executable" projectType="cdt.managedbuild.target.gnu.exe"/>
</storageModule>
<storageModule moduleId="scannerConfiguration"> <storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
@ -166,8 +171,8 @@
<parser enabled="true"/> <parser enabled="true"/>
</scannerInfoProvider> </scannerInfoProvider>
</profile> </profile>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.375046330;cdt.managedbuild.config.gnu.exe.release.375046330.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.1192057398;cdt.managedbuild.tool.gnu.cpp.compiler.input.1743926560"> <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.375046330;cdt.managedbuild.config.gnu.exe.release.375046330.;cdt.managedbuild.tool.gnu.c.compiler.exe.release.1809941717;cdt.managedbuild.tool.gnu.c.compiler.input.329001407">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider> <buildOutputProvider>
<openAction enabled="true" filePath=""/> <openAction enabled="true" filePath=""/>
@ -249,8 +254,8 @@
</scannerInfoProvider> </scannerInfoProvider>
</profile> </profile>
</scannerConfigBuildInfo> </scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.375046330;cdt.managedbuild.config.gnu.exe.release.375046330.;cdt.managedbuild.tool.gnu.c.compiler.exe.release.1809941717;cdt.managedbuild.tool.gnu.c.compiler.input.329001407"> <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.375046330;cdt.managedbuild.config.gnu.exe.release.375046330.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.1192057398;cdt.managedbuild.tool.gnu.cpp.compiler.input.1743926560">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider> <buildOutputProvider>
<openAction enabled="true" filePath=""/> <openAction enabled="true" filePath=""/>
@ -499,9 +504,4 @@
</profile> </profile>
</scannerConfigBuildInfo> </scannerConfigBuildInfo>
</storageModule> </storageModule>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="edexBridge.cdt.managedbuild.target.gnu.exe.32670172" name="Executable" projectType="cdt.managedbuild.target.gnu.exe"/>
</storageModule>
</cproject> </cproject>

View file

@ -3,21 +3,21 @@
* *
* Created on: Oct 8, 2009 * Created on: Oct 8, 2009
* Author: brockwoo * Author: brockwoo
* Updated on: June 21, 2013 (Re-written to work with the qpid messaging api)
* Author: bkowal
*/ */
// START SNIPPET: demo #include <qpid/messaging/Connection.h>
#include <qpid/messaging/Session.h>
#include <qpid/client/Connection.h> #include <qpid/messaging/Message.h>
#include <qpid/client/Session.h> #include <qpid/messaging/Sender.h>
#include <qpid/client/Message.h>
#include <qpid/client/MessageListener.h>
#include <qpid/client/SubscriptionManager.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ipc.h> #include <sys/ipc.h>
#include <sys/sem.h> #include <sys/sem.h>
#include <sys/shm.h> #include <sys/shm.h>
#include <stdlib.h> #include <stdlib.h>
#include <iostream> #include <iostream>
#include <sstream>
#include <memory> #include <memory>
#include <filel.h> #include <filel.h>
#include <ulog.h> #include <ulog.h>
@ -26,8 +26,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <list> #include <list>
using namespace qpid::client; using namespace qpid::messaging;
using namespace qpid::framing;
using namespace std; using namespace std;
class LdmProducer { class LdmProducer {
@ -35,23 +34,31 @@ private:
Connection connection; Connection connection;
Session session; Session session;
Sender sender;
bool useTopic; bool useTopic;
bool sessionTransacted; bool sessionTransacted;
bool isConnected; bool isConnected;
std::string brokerURI; std::string brokerURI;
int portNumber; int portNumber;
std::string username;
std::string password;
list<string> filenameList; list<string> filenameList;
list<string> headerList; list<string> headerList;
public: public:
LdmProducer(const std::string& brokerURI, int port = 5672, bool useTopic = LdmProducer(const std::string& brokerURI, int port = 5672,
false, bool sessionTransacted = false) { const std::string& username = "guest",
const std::string& password = "guest",
bool useTopic = false, bool sessionTransacted = false)
{
this->useTopic = useTopic; this->useTopic = useTopic;
this->sessionTransacted = sessionTransacted; this->sessionTransacted = sessionTransacted;
this->brokerURI = brokerURI; this->brokerURI = brokerURI;
this->isConnected = false; this->isConnected = false;
this->portNumber = port; this->portNumber = port;
this->username = username;
this->password = password;
} }
~LdmProducer() { ~LdmProducer() {
@ -82,20 +89,19 @@ public:
try { try {
while (!this->filenameList.empty()) { while (!this->filenameList.empty()) {
Message message; Message message;
message.getDeliveryProperties().setRoutingKey(
"external.dropbox");
fileLocation = this->filenameList.front(); fileLocation = this->filenameList.front();
fileHeader = this->headerList.front(); fileHeader = this->headerList.front();
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
long long current = (((long long) tv.tv_sec) * 1000000 uint64_t current = (((long long) tv.tv_sec) * 1000000
+ ((long long) tv.tv_usec)) / 1000; + ((long long) tv.tv_usec)) / 1000;
message.getDeliveryProperties().setDeliveryMode(PERSISTENT); message.setDurable(true);
message.setData(fileLocation); message.setContent(fileLocation);
message.getHeaders().setString("header", fileHeader); message.getProperties()["header"] = fileHeader;
message.getHeaders().setInt64("enqueueTime", current); message.getProperties()["enqueueTime"] = current;
session.messageTransfer(arg::content = message,
arg::destination = "amq.direct"); this->sender.send(message);
this->filenameList.pop_front(); this->filenameList.pop_front();
this->headerList.pop_front(); this->headerList.pop_front();
@ -103,7 +109,6 @@ public:
} }
} catch (const std::exception& error) { } catch (const std::exception& error) {
// Error occurred during communication. Clean up the connection and return the number of messages processed. // Error occurred during communication. Clean up the connection and return the number of messages processed.
uerror(error.what());
cleanup(); cleanup();
} }
@ -112,31 +117,94 @@ public:
private: private:
void cleanup() { void cleanup()
cout << "Cleaning up\n"; {
unotice ("Cleaning up");
// Destroy resources. // Destroy resources.
try { if (this->sender != 0)
session.close(); {
connection.close(); try
} catch (const std::exception& error) { {
this->isConnected = false; this->sender.close();
this->sender = 0;
}
catch (const std::exception& error)
{
uwarn(error.what());
}
}
if (this->session != 0)
{
try
{
this->session.close();
this->session = 0;
}
catch (const std::exception& error)
{
uwarn(error.what());
}
}
if (this->connection != 0)
{
try
{
this->connection.close();
this->connection = 0;
}
catch (const std::exception& error)
{
uwarn(error.what());
}
} }
this->isConnected = false; this->isConnected = false;
} }
bool connect() { bool connect()
if (this->isConnected) { {
if (this->isConnected)
{
return this->isConnected; return this->isConnected;
} }
try { try
this->connection.open(brokerURI, portNumber); {
this->session = this->connection.newSession(); // initialize
session.queueDeclare(arg::queue = "external.dropbox", arg::durable=true); this->connection = 0;
session.exchangeBind(arg::exchange = "amq.direct", arg::queue this->session = 0;
= "external.dropbox", arg::bindingKey = "external.dropbox"); this->sender = 0;
std::stringstream qpidURLBuilder;
qpidURLBuilder << "amqp:tcp:";
qpidURLBuilder << this->brokerURI;
qpidURLBuilder << ":";
qpidURLBuilder << this->portNumber;
std::string qpidURL = qpidURLBuilder.str();
std::stringstream connectionOptionsBuilder;
connectionOptionsBuilder << "{sasl-mechanism:PLAIN,username:";
connectionOptionsBuilder << this->username;
connectionOptionsBuilder << ",password:";
connectionOptionsBuilder << this->password;
connectionOptionsBuilder << "}";
std::string connectionOptions = connectionOptionsBuilder.str();
this->connection = Connection(qpidURL, connectionOptions);
this->connection.open();
std::string address = "external.dropbox; {node:{type:queue,durable:true,x-bindings:"
"[{exchange:amq.direct,queue:external.dropbox,key:external.dropbox}]}}";
this->session = this->connection.createSession();
this->sender = this->session.createSender(address);
this->isConnected = true; this->isConnected = true;
} catch (const std::exception& error) { }
catch (const std::exception& error)
{
uerror(error.what());
this->isConnected = false; this->isConnected = false;
} }
return this->isConnected; return this->isConnected;
@ -231,6 +299,8 @@ int main(int argc, char* argv[]) {
int loggingToStdErr = 0; int loggingToStdErr = 0;
std::string brokerURI = "127.0.0.1"; std::string brokerURI = "127.0.0.1";
int port = 5672; int port = 5672;
std::string username = "guest";
std::string password = "guest";
{ {
extern char *optarg; extern char *optarg;
@ -281,7 +351,6 @@ int main(int argc, char* argv[]) {
// createQueue to be used in both consumer an producer. // createQueue to be used in both consumer an producer.
//============================================================ //============================================================
bool useTopics = false; bool useTopics = false;
//bool sessionTransacted = false;
int shmid; int shmid;
int semid; int semid;
@ -309,7 +378,7 @@ int main(int argc, char* argv[]) {
messageCursor = (edex_message *) shmat(shmid, (void *) 0, 0); messageCursor = (edex_message *) shmat(shmid, (void *) 0, 0);
LdmProducer producer(brokerURI, port, useTopics); LdmProducer producer(brokerURI, port, username, password, useTopics);
for (;;) { for (;;) {
if (hupped) { if (hupped) {

View file

@ -20,8 +20,9 @@ Vendor: Raytheon
Packager: Bryan Kowal Packager: Bryan Kowal
AutoReq: no AutoReq: no
Requires: qpid-cpp-client-devel Requires: awips2-qpid-lib
Requires: zlib-devel Requires: zlib-devel
requires: awips2-python
provides: awips2-ldm provides: awips2-ldm
provides: awips2-base-component provides: awips2-base-component
@ -76,7 +77,7 @@ fi
_ldm_destination=%{_build_root}/usr/local/ldm _ldm_destination=%{_build_root}/usr/local/ldm
_ldm_destination_source=${_ldm_destination}/SOURCES _ldm_destination_source=${_ldm_destination}/SOURCES
_NATIVELIB_PROJECTS=( 'edexBridge' 'decrypt_file' 'org.apache.qpid' ) _NATIVELIB_PROJECTS=( 'edexBridge' 'decrypt_file' )
_RPM_directory=%{_baseline_workspace}/rpms _RPM_directory=%{_baseline_workspace}/rpms
_Installer_ldm=${_RPM_directory}/awips2.core/Installer.ldm _Installer_ldm=${_RPM_directory}/awips2.core/Installer.ldm
@ -271,28 +272,10 @@ if [ ${_myHost} != "cpsbn1" -a ${_myHost} != "cpsbn2" -a ${_myHost} != "dx1" -a
fi fi
popd > /dev/null 2>&1 popd > /dev/null 2>&1
# extract qpid libraries; build decrypt_file & edexBridge # build decrypt_file & edexBridge
pushd . > /dev/null 2>&1 pushd . > /dev/null 2>&1
cd ${_ldm_dir}/SOURCES cd ${_ldm_dir}/SOURCES
# determine which lib directory to use
_arch=`uname -i`
_qpid_lib_dir="lib"
if [ "${_arch}" = "x86_64" ]; then
_qpid_lib_dir="lib64"
fi
/bin/tar -xvf org.apache.qpid.tar org.apache.qpid/${_qpid_lib_dir}/*
if [ $? -ne 0 ]; then
echo "FATAL: failed to extract the qpid libraries!"
exit 1
fi
cp -Pf org.apache.qpid/${_qpid_lib_dir}/* ${_ldm_root_dir}/lib
if [ $? -ne 0 ]; then
echo "FATAL: failed to copy the qpid libraries to the ldm lib directory."
exit 1
fi
/bin/tar -xf decrypt_file.tar /bin/tar -xf decrypt_file.tar
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "FATAL: failed to untar decrypt_file.tar!" echo "FATAL: failed to untar decrypt_file.tar!"
@ -337,10 +320,10 @@ export _current_dir=`pwd`
su ldm -lc "cd ${_current_dir}; g++ edexBridge.cpp -I${_ldm_root_dir}/src/pqact \ su ldm -lc "cd ${_current_dir}; g++ edexBridge.cpp -I${_ldm_root_dir}/src/pqact \
-I${_ldm_root_dir}/include \ -I${_ldm_root_dir}/include \
-I${_ldm_root_dir}/src \ -I${_ldm_root_dir}/src \
-I/usr/include/qpid \ -I/awips2/qpid/include \
-L${_ldm_root_dir}/lib \ -L${_ldm_root_dir}/lib \
-L%{_libdir} \ -L/awips2/qpid/lib \
-l ldm -l xml2 -l qpidclient -l qpidcommon -o edexBridge" > \ -l ldm -l xml2 -l qpidclient -l qpidmessaging -l qpidcommon -l qpidtypes -o edexBridge" > \
edexBridge.log 2>&1 edexBridge.log 2>&1
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "FATAL: failed to build edexBridge!" echo "FATAL: failed to build edexBridge!"

View file

@ -1 +1,3 @@
/usr/local/ldm/lib /usr/local/ldm/lib
/awips2/qpid/lib
/awips2/python/lib

View file

@ -69,7 +69,7 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
./configure --prefix=%{_qpid_build_loc}/awips2/qpid ./configure --prefix=%{_qpid_build_loc}/awips2/qpid --without-sasl
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi

View file

@ -26,7 +26,7 @@ function buildQPID()
return 1 return 1
fi fi
/bin/bash build.sh /bin/bash build.sh 0.18
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: Failed to build the qpid rpms." echo "ERROR: Failed to build the qpid rpms."
return 1 return 1
@ -39,16 +39,9 @@ function buildQPID()
exit 1 exit 1
fi fi
fi fi
if [ ! -d ${AWIPSII_TOP_DIR}/RPMS/i386 ]; then
mkdir -p ${AWIPSII_TOP_DIR}/RPMS/i386
if [ $? -ne 0 ]; then
exit 1
fi
fi
pushd . > /dev/null 2>&1
# Copy the 0.18 qpid rpms # Copy the 0.18 qpid rpms
cd 0.18/RPMS/noarch cd ${WORKSPACE}/rpms/awips2.qpid/0.18/RPMS/noarch
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: Failed to build Qpid v0.18." echo "ERROR: Failed to build Qpid v0.18."
return 1 return 1
@ -57,79 +50,46 @@ function buildQPID()
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
return 1 return 1
fi fi
cd ../x86_64
if [ $? -ne 0 ]; then # determine which qpid libs we need to copy
echo "ERROR: Failed to build Qpid v0.18 lib." _arch=`uname -i`
return 1
fi if [ "${_arch}" = "x86_64" ]; then
if [ ! -d ${AWIPSII_TOP_DIR}/RPMS/x86_64 ]; then if [ ! -d ${AWIPSII_TOP_DIR}/RPMS/x86_64 ]; then
mkdir -p ${AWIPSII_TOP_DIR}/RPMS/x86_64 mkdir -p ${AWIPSII_TOP_DIR}/RPMS/x86_64
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
fi fi
cd ${WORKSPACE}/rpms/awips2.qpid/0.18/RPMS/x86_64
if [ $? -ne 0 ]; then
echo "ERROR: Failed to build Qpid v0.18."
return 1
fi
/bin/cp -v *.rpm ${AWIPSII_TOP_DIR}/RPMS/x86_64 /bin/cp -v *.rpm ${AWIPSII_TOP_DIR}/RPMS/x86_64
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: Failed to build Qpid v0.18 lib."
return 1 return 1
fi fi
popd > /dev/null 2>&1 else
if [ ! -d ${AWIPSII_TOP_DIR}/RPMS/i386 ]; then
pushd . > /dev/null 2>&1 mkdir -p ${AWIPSII_TOP_DIR}/RPMS/i386
# Copy the 0.7 qpid rpms
cd 0.7/RPMS/i386
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: Failed to build Qpid v0.7." exit 1
return 1 fi
fi fi
# Copy the qpid rpms from the local build directory to the rpm cd ${WORKSPACE}/rpms/awips2.qpid/0.18/RPMS/i386
# "staging" directory.
/bin/cp -v awips2-qpid-client-0.7.946106-*.i386.rpm \
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: Failed to build Qpid v0.18."
return 1 return 1
fi fi
/bin/cp -v awips2-qpid-server-0.7.946106-*.i386.rpm \ /bin/cp -v *.rpm ${AWIPSII_TOP_DIR}/RPMS/i386
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then
return 1
fi
/bin/cp -v awips2-qpid-server-store-0.7.946106-*.i386.rpm \
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then
return 1
fi
# if the -ade argument has been specified. Also copy the qpid
# devel rpms.
if [ "${1}" = "-ade" ]; then
/bin/cp -v awips2-qpid-client-devel-0.7.946106-*.i386.rpm \
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then
return 1
fi
/bin/cp -v awips2-qpid-client-devel-docs-0.7.946106-*.i386.rpm \
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then
return 1
fi
/bin/cp -v awips2-qpid-server-devel-0.7.946106-*.i386.rpm \
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then
return 1
fi
/bin/cp qpid-cpp-mrg-debuginfo-0.7.946106-*.i386.rpm \
${AWIPSII_TOP_DIR}/RPMS/i386/
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
return 1 return 1
fi fi
fi fi
popd > /dev/null 2>&1
popd > /dev/null 2>&1 popd > /dev/null 2>&1
return 0 return 0