1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <thrift/server/TClientInfo.h>
21 
22 namespace apache { namespace thrift { namespace server {
23 
24 using namespace apache::thrift;
25 using namespace apache::thrift::transport;
26 
TClientInfoConnection()27 TClientInfoConnection::TClientInfoConnection() {
28   call_[kNameLen - 1] = '\0';    // insure NUL terminator is there
29   eraseAddr();
30   eraseCall();
31 }
32 
recordAddr(const sockaddr * addr)33 void TClientInfoConnection::recordAddr(const sockaddr* addr) {
34   eraseAddr();
35   initTime();
36   ncalls_ = 0;
37   if (addr != nullptr) {
38     if (addr->sa_family == AF_INET) {
39       memcpy((void*)&addr_.ipv4, (const void *)addr, sizeof(sockaddr_in));
40     }
41     else if (addr->sa_family == AF_INET6) {
42       memcpy((void*)&addr_.ipv6, (const void *)addr, sizeof(sockaddr_in6));
43     }
44   }
45 }
46 
eraseAddr()47 void TClientInfoConnection::eraseAddr() {
48   addr_.ipv4.sin_family = AF_UNSPEC;
49 }
50 
getAddr(char * buf,int len) const51 const char* TClientInfoConnection::getAddr(char* buf, int len) const {
52   switch (addr_.ipv4.sin_family) {
53   case AF_INET:
54     return inet_ntop(AF_INET, &addr_.ipv4.sin_addr, buf, len);
55   case AF_INET6:
56     return inet_ntop(AF_INET6, &addr_.ipv6.sin6_addr, buf, len);
57   default:
58     return nullptr;
59   }
60 }
61 
recordCall(const char * name)62 void TClientInfoConnection::recordCall(const char* name) {
63   strncpy(call_, name, kNameLen - 1);   // NUL terminator set in constructor
64   ncalls_++;
65 }
66 
eraseCall()67 void TClientInfoConnection::eraseCall() {
68   call_[0] = '\0';
69 }
70 
getCall() const71 const char* TClientInfoConnection::getCall() const {
72   if (call_[0] == '\0') {
73       return nullptr;
74   }
75   return call_;
76 }
77 
getTime(timespec * time) const78 void TClientInfoConnection::getTime(timespec* time) const {
79   *time = time_;
80 }
81 
getNCalls() const82 uint64_t TClientInfoConnection::getNCalls() const {
83   return ncalls_;
84 }
85 
initTime()86 void TClientInfoConnection::initTime() {
87   clock_gettime(CLOCK_REALTIME, &time_);
88 }
89 
90 
getConnection(int fd,bool grow)91 TClientInfoConnection* TClientInfo::getConnection(int fd, bool grow) {
92   if (fd < 0 || (!grow && fd >= info_.size())) {
93     return nullptr;
94   }
95   return &info_[fd];
96 }
97 
size() const98 size_t TClientInfo::size() const {
99     return info_.size();
100 }
101 
createContext(boost::shared_ptr<TProtocol> input,boost::shared_ptr<TProtocol> output)102 void* TClientInfoServerHandler::createContext(boost::shared_ptr<TProtocol> input,
103                                               boost::shared_ptr<TProtocol> output) {
104   (void)input;
105   (void)output;
106   return (void*) new Connect(&clientInfo_);
107 }
108 
deleteContext(void * connectionContext,boost::shared_ptr<TProtocol> input,boost::shared_ptr<TProtocol> output)109 void TClientInfoServerHandler::deleteContext(void* connectionContext,
110                                              boost::shared_ptr<TProtocol> input,
111                                              boost::shared_ptr<TProtocol> output) {
112   Connect* call = static_cast<Connect*>(connectionContext);
113   if (call->callInfo_) {
114     call->callInfo_->eraseCall();
115   }
116   delete call;
117 }
118 
processContext(void * connectionContext,shared_ptr<TTransport> transport)119 void TClientInfoServerHandler::processContext(void* connectionContext,
120                                               shared_ptr<TTransport> transport) {
121   Connect* call = static_cast<Connect*>(connectionContext);
122   if (call->callInfo_ == nullptr) {
123     if (typeid(*(transport.get())) == typeid(TSocket)) {
124       TSocket* tsocket = static_cast<TSocket*>(transport.get());
125       int fd = tsocket->getSocketFD();
126       if (fd < 0) {
127         return;
128       }
129       call->callInfo_ = call->clientInfo_->getConnection(fd, true);
130       assert(call->callInfo_ != nullptr);
131       socklen_t len;
132         call->callInfo_->recordAddr(tsocket->getCachedAddress(&len));
133     }
134   }
135 }
136 
getStatsStrings(vector<string> & result)137 void TClientInfoServerHandler::getStatsStrings(vector<string>& result) {
138   result.clear();
139   timespec now;
140   clock_gettime(CLOCK_REALTIME, &now);
141 
142   for (int i = 0; i < clientInfo_.size(); ++i) {
143     TClientInfoConnection* info = clientInfo_.getConnection(i, false);
144     const char* callStr = info->getCall();
145     if (callStr == nullptr) {
146       continue;
147     }
148 
149     char addrBuf[INET6_ADDRSTRLEN];
150     const char* addrStr = info->getAddr(addrBuf, sizeof addrBuf);
151     if (addrStr == nullptr) {
152       // cerr << "no addr!" << endl;
153       continue;
154     }
155 
156     timespec start;
157     info->getTime(&start);
158     double secs = (double)(now.tv_sec - start.tv_sec) + (now.tv_nsec - start.tv_nsec)*0.000000001;
159 
160     char buf[256];
161     snprintf(buf, sizeof buf, "%d %s %s %.3f %llu", i, addrStr, callStr, secs,
162              (uint64_t)info->getNCalls());
163 
164     result.push_back(buf);
165   }
166 }
167 
getContext(const char * fn_name,void * serverContext)168 void* TClientInfoCallHandler::getContext(const char* fn_name, void* serverContext) {
169   if (serverContext) {
170     TClientInfoConnection* callInfo =  static_cast<TClientInfoServerHandler::Connect*>(serverContext)->callInfo_;
171     if (callInfo != nullptr) {
172       callInfo->recordCall(fn_name);
173     }
174   }
175   return nullptr;
176 }
177 
178 } } } // namespace apache::thrift::server
179