RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* redis_temporary_server.h -*- C++ -*- 00002 Jeremy Barnes, 19 October 2012 00003 Copyright (c) 2012 Datacratic Inc. All rights reserved. 00004 00005 A temporary server for testing of redis-based services. Starts one up in a 00006 temporary directory and gives the uri to connect to. 00007 */ 00008 00009 #include "jml/utils/environment.h" 00010 #include "jml/utils/file_functions.h" 00011 #include "jml/arch/timers.h" 00012 #include "soa/service/redis.h" 00013 #include <sys/stat.h> 00014 #include <sys/types.h> 00015 #include <sys/wait.h> 00016 #include <sys/un.h> 00017 #include <sys/socket.h> 00018 #include <sys/prctl.h> 00019 #include <signal.h> 00020 00021 namespace Redis { 00022 00023 struct RedisTemporaryServer : boost::noncopyable { 00024 00025 RedisTemporaryServer(std::string uniquePath = "") 00026 { 00027 using namespace std; 00028 if (uniquePath == "") { 00029 ML::Env_Option<std::string> tmpDir("TMP", "./tmp"); 00030 uniquePath = ML::format("%s/redis-temporary-server-%d", 00031 tmpDir.get(), getpid()); 00032 cerr << "starting redis temporary server under unique path " 00033 << uniquePath << endl; 00034 } 00035 00036 this->uniquePath = uniquePath; 00037 start(); 00038 } 00039 00040 ~RedisTemporaryServer() 00041 { 00042 shutdown(); 00043 } 00044 00045 void start() 00046 { 00047 using namespace std; 00048 00049 // Check the unique path 00050 if (uniquePath == "" || uniquePath[0] == '/' || uniquePath == "." 00051 || uniquePath == "..") 00052 throw ML::Exception("unacceptable unique path"); 00053 00054 // 1. Create the directory 00055 00056 #if 0 00057 char cwd[1024]; 00058 00059 if (uniquePath[0] != '/') 00060 uniquePath = getcwd(cwd, 1024) + string("/") + uniquePath; 00061 00062 cerr << "uniquePath = " << uniquePath << endl; 00063 #endif 00064 00065 // First check that it doesn't exist 00066 struct stat stats; 00067 int res = stat(uniquePath.c_str(), &stats); 00068 if (res != -1 || (errno != EEXIST && errno != ENOENT)) 00069 throw ML::Exception(errno, "unique path " + uniquePath 00070 + " already exists"); 00071 00072 res = system(ML::format("mkdir -p %s", uniquePath).c_str()); 00073 if (res == -1) 00074 throw ML::Exception(errno, "couldn't mkdir"); 00075 00076 string unixPath = uniquePath + "/redis-socket"; 00077 00078 int UNIX_PATH_MAX=108; 00079 00080 if (unixPath.size() >= UNIX_PATH_MAX) 00081 throw ML::Exception("unix socket path is too long"); 00082 00083 // Create unix socket 00084 res = mknod(unixPath.c_str(), 0777 | S_IFIFO, 0); 00085 if (res == -1) 00086 throw ML::Exception(errno, "couldn't create unix socket for Redis"); 00087 00088 //ML::set_permissions(unixPath, "777", ""); 00089 00090 // 2. Start the server 00091 int pid = fork(); 00092 if (pid == -1) 00093 throw ML::Exception(errno, "fork"); 00094 if (pid == 0) { 00095 int res = prctl(PR_SET_PDEATHSIG, SIGHUP); 00096 if(res == -1) { 00097 throw ML::Exception(errno, "prctl failed"); 00098 } 00099 00100 signal(SIGTERM, SIG_DFL); 00101 signal(SIGKILL, SIG_DFL); 00102 00103 cerr << "running redis" << endl; 00104 res = execlp("redis-server", 00105 "redis-server", 00106 "--port", "0", 00107 "--unixsocket", "./redis-socket", 00108 "--dir", uniquePath.c_str(), 00109 (char *)0); 00110 if (res == -1) 00111 throw ML::Exception(errno, "redis failed to start"); 00112 00113 throw ML::Exception(errno, "redis failed to start"); 00114 } 00115 else { 00116 serverPid = pid; 00117 } 00118 00119 { 00120 // 3. Connect to the server to make sure it works 00121 int sock = socket(AF_UNIX, SOCK_STREAM, 0); 00122 if (sock == -1) 00123 throw ML::Exception(errno, "socket"); 00124 00125 sockaddr_un addr; 00126 addr.sun_family = AF_UNIX; 00127 strcpy(addr.sun_path, unixPath.c_str()); 00128 00129 // Wait for it to start up 00130 for (unsigned i = 0; i < 1000; ++i) { 00131 res = connect(sock, (const sockaddr *)&addr, sizeof(addr)); 00132 if (res == 0) break; 00133 if (res == -1 && errno != ECONNREFUSED) 00134 throw ML::Exception(errno, "connect"); 00135 00136 ML::sleep(0.01); 00137 } 00138 00139 if (res != 0) 00140 throw ML::Exception("redis didn't start up in 10 seconds"); 00141 00142 close(sock); 00143 } 00144 00145 cerr << "address is " << unixPath << endl; 00146 00147 this->address_ = Address::unix(unixPath); 00148 } 00149 00150 void shutdown() 00151 { 00152 if (serverPid == -1) 00153 return; 00154 00155 // Stop boost test framework from interpreting this as a problem... 00156 signal(SIGCHLD, SIG_DFL); 00157 00158 int res = kill(serverPid, SIGTERM); 00159 if (res == -1) 00160 throw ML::Exception(errno, "kill redis"); 00161 00162 int status = 0; 00163 res = waitpid(serverPid, &status, 0); 00164 if (res == -1) 00165 throw ML::Exception(errno, "wait for redis shutdown"); 00166 00167 this->address_ = Address(); 00168 00169 serverPid = -1; 00170 00171 if (uniquePath != "") { 00172 using namespace std; 00173 cerr << "removing " << uniquePath << endl; 00174 int rmstatus = system(("rm -rf " + uniquePath).c_str()); 00175 if (rmstatus) 00176 throw ML::Exception(errno, "removing redis path"); 00177 } 00178 } 00179 00180 Address address() const 00181 { 00182 return address_; 00183 } 00184 00185 operator Address() const 00186 { 00187 return address(); 00188 } 00189 00190 Address address_; 00191 std::string uniquePath; 00192 int serverPid; 00193 }; 00194 00195 } // namespace Redis 00196