1 /*
2   +----------------------------------------------------------------------+
3   | Swoole                                                               |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 2.0 of the Apache license,    |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | http://www.apache.org/licenses/LICENSE-2.0.html                      |
9   | If you did not receive a copy of the Apache2.0 license and are unable|
10   | to obtain it through the world-wide-web, please send a note to       |
11   | license@swoole.com so we can mail you a copy immediately.            |
12   +----------------------------------------------------------------------+
13   | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
14   +----------------------------------------------------------------------+
15  */
16 
17 #include "php_swoole_server.h"
18 #include "swoole_process_pool.h"
19 #include "php_swoole_http.h"
20 #include "php_swoole_x_arginfo.h"
21 
22 #include <sstream>
23 #include <thread>
24 
25 #include "nlohmann/json.hpp"
26 
27 using json = nlohmann::json;
28 
29 namespace swoole {
30 
31 #ifdef TCP_INFO
32 static json get_socket_info(int fd);
33 #endif
34 
handle_get_all_unix_sockets(Server * _server,const std::string & msg)35 static std::string handle_get_all_unix_sockets(Server *_server, const std::string &msg) {
36     auto _result = json::parse(msg);
37     if (!_result.is_object() || _result.find("type") == _result.end()) {
38         json return_value{
39             {"data", "require parameter type"},
40             {"code", 4003},
41         };
42         return return_value.dump();
43     }
44 
45     std::string _type = _result["type"];
46     Worker *workers;
47     uint32_t worker_num;
48 
49     if (_type == "event") {
50         workers = _server->gs->event_workers.workers;
51         worker_num = _server->worker_num;
52     } else {
53         workers = _server->gs->task_workers.workers;
54         worker_num = _server->task_worker_num;
55     }
56 
57     json sockets = json::array();
58 
59     SW_LOOP_N(worker_num) {
60         auto master_socket = workers[i].pipe_object->get_socket(true);
61         json master_socket_info = json::object({
62             {"fd", master_socket->fd},
63             {"events", master_socket->events},
64             {"total_recv_bytes", master_socket->total_recv_bytes},
65             {"total_send_bytes", master_socket->total_send_bytes},
66             {"out_buffer_size", master_socket->out_buffer ? master_socket->out_buffer->length() : 0},
67         });
68         sockets.push_back(master_socket_info);
69 
70         auto worker_socket = workers[i].pipe_object->get_socket(false);
71         json worker_socket_info = json::object({
72             {"fd", worker_socket->fd},
73             {"events", worker_socket->events},
74             {"total_recv_bytes", worker_socket->total_recv_bytes},
75             {"total_send_bytes", worker_socket->total_send_bytes},
76             {"out_buffer_size", worker_socket->out_buffer ? worker_socket->out_buffer->length() : 0},
77         });
78         sockets.push_back(worker_socket_info);
79     }
80 
81     json return_value{
82         {"data", sockets},
83         {"code", 0},
84     };
85     return return_value.dump();
86 }
87 
handle_get_all_sockets(Server *,const std::string & msg)88 static std::string handle_get_all_sockets(Server *, const std::string &msg) {
89     if (sw_reactor() == nullptr) {
90         json return_value{
91             {"data", "No event loop created"},
92             {"code", 4004},
93         };
94         return return_value.dump();
95     }
96 
97     json sockets = json::array();
98     sw_reactor()->foreach_socket([&sockets](int fd, network::Socket *socket) {
99         network::Address addr{};
100         if (socket->socket_type > SW_SOCK_UNIX_DGRAM || socket->socket_type < SW_SOCK_TCP) {
101 #ifdef SO_DOMAIN
102             struct stat fdstat;
103             if (fstat(fd, &fdstat) == -1) {
104                 return;
105             }
106             mode_t type = fdstat.st_mode & S_IFMT;
107             if (type == S_IFSOCK) {
108                 int domain;
109                 if (socket->get_option(SOL_SOCKET, SO_DOMAIN, &domain) < 0) {
110                     return;
111                 }
112                 int type;
113                 if (socket->get_option(SOL_SOCKET, SO_TYPE, &type) < 0) {
114                     return;
115                 }
116                 addr.type = network::Socket::convert_to_type(domain, type);
117                 socket->get_name(&addr);
118             }
119 #else
120             return;
121 #endif
122         } else {
123             addr = socket->info;
124         }
125         json info = json::object({
126             {"fd", socket->fd},
127             {"address", addr.get_ip()},
128             {"port", addr.get_port()},
129             {"events", socket->events},
130             {"socket_type", socket->socket_type},
131             {"fd_type", socket->fd_type},
132             {"total_recv_bytes", socket->total_recv_bytes},
133             {"total_send_bytes", socket->total_send_bytes},
134             {"out_buffer_size", socket->out_buffer ? socket->out_buffer->length() : 0},
135         });
136         sockets.push_back(info);
137     });
138 
139     json return_value{
140         {"data", sockets},
141         {"code", 0},
142     };
143     return return_value.dump();
144 }
145 
handle_get_all_commands(Server * serv,const std::string & msg)146 static std::string handle_get_all_commands(Server *serv, const std::string &msg) {
147     json command_list = json::array();
148     for (auto kv : serv->commands) {
149         json info = json::object({
150             {"id", kv.second.id},
151             {"name", kv.second.name},
152             {"accepted_process_types", kv.second.accepted_process_types},
153         });
154         command_list.push_back(info);
155     };
156     json return_value{
157         {"data", command_list},
158         {"code", 0},
159     };
160     return return_value.dump();
161 }
162 
163 #ifdef TCP_INFO
get_socket_info(int fd)164 static json get_socket_info(int fd) {
165     struct tcp_info info;
166     socklen_t len = sizeof(info);
167     if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &len) < 0) {
168         json return_value{
169             {"data", "failed to getsockopt(TCP_INFO) for socket"},
170             {"code", 5001},
171         };
172         return return_value.dump();
173     }
174 #if defined(__FreeBSD__)
175     json jinfo{
176         {"state", info.tcpi_state},
177         {"ca_state", info.__tcpi_ca_state},
178         {"retransmits", info.__tcpi_retransmits},
179         {"probes", info.__tcpi_probes},
180         {"backoff", info.__tcpi_backoff},
181         {"options", info.tcpi_options},
182         {"snd_wscale", uint8_t(info.tcpi_snd_wscale)},
183         {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)},
184         {"rto", info.tcpi_rto},
185         {"ato", info.__tcpi_ato},
186         {"snd_mss", info.tcpi_snd_mss},
187         {"rcv_mss", info.tcpi_rcv_mss},
188         {"unacked", info.__tcpi_unacked},
189         {"sacked", info.__tcpi_sacked},
190         {"lost", info.__tcpi_lost},
191         {"retrans", info.__tcpi_retrans},
192         {"fackets", info.__tcpi_fackets},
193         {"last_data_sent", info.__tcpi_last_data_sent},
194         {"last_ack_sent", info.__tcpi_last_ack_sent},
195         {"last_data_recv", info.tcpi_last_data_recv},
196         {"last_ack_recv", info.__tcpi_last_ack_recv},
197         {"pmtu", info.__tcpi_pmtu},
198         {"rcv_ssthresh", info.__tcpi_rcv_ssthresh},
199         {"rtt", info.tcpi_rtt},
200         {"rttvar", info.tcpi_rttvar},
201         {"snd_ssthresh", info.tcpi_snd_ssthresh},
202         {"snd_cwnd", info.tcpi_snd_cwnd},
203         {"advmss", info.__tcpi_advmss},
204         {"reordering", info.__tcpi_reordering},
205         {"rcv_rtt", info.__tcpi_rcv_rtt},
206         {"rcv_space", info.tcpi_rcv_space},
207         {"total_retrans", 0},
208     };
209 #else
210     json jinfo{
211         {"state", info.tcpi_state},
212         {"ca_state", info.tcpi_ca_state},
213         {"retransmits", info.tcpi_retransmits},
214         {"probes", info.tcpi_probes},
215         {"backoff", info.tcpi_backoff},
216         {"options", info.tcpi_options},
217         {"snd_wscale", uint8_t(info.tcpi_snd_wscale)},
218         {"rcv_wscale", uint8_t(info.tcpi_rcv_wscale)},
219         {"rto", info.tcpi_rto},
220         {"ato", info.tcpi_ato},
221         {"snd_mss", info.tcpi_snd_mss},
222         {"rcv_mss", info.tcpi_rcv_mss},
223         {"unacked", info.tcpi_unacked},
224         {"sacked", info.tcpi_sacked},
225         {"lost", info.tcpi_lost},
226         {"retrans", info.tcpi_retrans},
227         {"fackets", info.tcpi_fackets},
228         {"last_data_sent", info.tcpi_last_data_sent},
229         {"last_ack_sent", info.tcpi_last_ack_sent},
230         {"last_data_recv", info.tcpi_last_data_recv},
231         {"last_ack_recv", info.tcpi_last_ack_recv},
232         {"pmtu", info.tcpi_pmtu},
233         {"rcv_ssthresh", info.tcpi_rcv_ssthresh},
234         {"rtt", info.tcpi_rtt},
235         {"rttvar", info.tcpi_rttvar},
236         {"snd_ssthresh", info.tcpi_snd_ssthresh},
237         {"snd_cwnd", info.tcpi_snd_cwnd},
238         {"advmss", info.tcpi_advmss},
239         {"reordering", info.tcpi_reordering},
240         {"rcv_rtt", info.tcpi_rcv_rtt},
241         {"rcv_space", info.tcpi_rcv_space},
242         {"total_retrans", info.tcpi_total_retrans},
243     };
244 #endif
245     return jinfo;
246 }
247 #endif
248 
get_connection_info(Server * serv,Connection * conn)249 static json get_connection_info(Server *serv, Connection *conn) {
250     auto server_socket = serv->get_port_by_server_fd(conn->server_fd)->socket;
251     json info = json::object({
252         {"session_id", conn->session_id},
253         {"reactor_id", conn->reactor_id},
254         {"fd", conn->fd},
255         {"server_port",
256          std::string(server_socket->info.get_ip()) + ":" + std::to_string(server_socket->info.get_port())},
257         {"address", conn->info.get_ip()},
258         {"port", conn->info.get_port()},
259         {"overflow", conn->overflow},
260         {"connect_time", conn->connect_time},
261         {"last_recv_time", conn->last_recv_time},
262         {"last_send_time", conn->last_send_time},
263         {"last_dispatch_time", conn->last_dispatch_time},
264         {"recv_queued_bytes", conn->recv_queued_bytes},
265         {"send_queued_bytes", conn->send_queued_bytes},
266         {"total_recv_bytes", conn->socket->total_recv_bytes},
267         {"total_send_bytes", conn->socket->total_send_bytes},
268         {"uid", conn->uid},
269     });
270     return info;
271 }
272 
handle_get_socket_info(Server * serv,const std::string & msg)273 static std::string handle_get_socket_info(Server *serv, const std::string &msg) {
274     auto _result = json::parse(msg);
275     if (!_result.is_object() || _result.find("fd") == _result.end()) {
276         json return_value{
277             {"data", "require parameter fd"},
278             {"code", 4003},
279         };
280         return return_value.dump();
281     }
282 
283 #ifndef TCP_INFO
284     json return_value{
285         {"data", "platform unsupported"},
286         {"code", 5001},
287     };
288 #else
289     std::string _fd = _result["fd"];
290     int fd = std::atoi(_fd.c_str());
291     json return_value{
292         {"data", get_socket_info(fd)},
293         {"code", 0},
294     };
295 #endif
296     return return_value.dump();
297 }
298 
handle_get_thread_info(Server * serv,const std::string & msg)299 static std::string handle_get_thread_info(Server *serv, const std::string &msg) {
300     ReactorThread *thread = serv->get_thread(SwooleTG.id);
301     std::stringstream ss;
302     ss << std::this_thread::get_id();
303     json jinfo{
304         {"tid", ss.str()},
305         {"id", thread->id},
306         {"dispatch_count", thread->dispatch_count},
307         {"event_num", SwooleTG.reactor->get_event_num()},
308         {"timer_num", SwooleTG.timer ? SwooleTG.timer->count() : 0},
309     };
310     json return_value{
311         {"data", jinfo},
312         {"code", 0},
313     };
314     return return_value.dump();
315 }
316 
handle_get_manager_info(Server * serv,const std::string & msg)317 static std::string handle_get_manager_info(Server *serv, const std::string &msg) {
318     ProcessPool *pool = (ProcessPool *) &serv->gs->event_workers;
319     json jinfo{
320         {"pid", getpid()},
321         {"reload_count", pool->reload_count},
322         {"reload_last_time", pool->reload_last_time},
323     };
324     json return_value{
325         {"data", jinfo},
326         {"code", 0},
327     };
328     return return_value.dump();
329 }
330 
get_socket_out_buffer_total_size()331 static size_t get_socket_out_buffer_total_size() {
332     if (!sw_reactor()) {
333         return 0;
334     }
335     size_t size = 0;
336     for (auto s : sw_reactor()->get_sockets()) {
337         if (s.second->out_buffer) {
338             size += s.second->out_buffer->length();
339         }
340     }
341     return size;
342 }
343 
handle_get_memory_info(Server * serv,const std::string & msg)344 static std::string handle_get_memory_info(Server *serv, const std::string &msg) {
345     bool is_thread = serv->is_reactor_thread();
346 
347     json jinfo{
348         {"server", sizeof(Server)},
349         {"workers", serv->get_all_worker_num() * sizeof(Worker)},
350         {"connection_list", serv->get_max_connection() * sizeof(Connection)},
351         {"session_list", SW_SESSION_LIST_SIZE * sizeof(Session)},
352         {"global_memory", dynamic_cast<GlobalMemory *>(sw_mem_pool())->get_memory_size()},
353         {"thread_global_memory", sw_tg_buffer()->size},
354         {"message_bus",
355          is_thread ? serv->get_thread(SwooleTG.id)->message_bus.get_memory_size()
356                    : serv->message_bus.get_memory_size()},
357         {"socket_list", sw_reactor() ? sw_reactor()->get_sockets().size() * sizeof(network::Socket) : 0},
358         {"socket_out_buffer", get_socket_out_buffer_total_size()},
359         {"php_memory", is_thread ? 0 : zend_memory_usage(true)},
360         {"http_buffer", swoole_http_buffer ? swoole_http_buffer->size : 0},
361         {"http_form_data_buffer", swoole_http_form_data_buffer ? swoole_http_form_data_buffer->size : 0},
362 #ifdef SW_HAVE_COMPRESSION
363         {"zlib_buffer", swoole_zlib_buffer ? swoole_zlib_buffer->size : 0},
364 #else
365         {"zlib_buffer", 0},
366 #endif
367     };
368     json return_value{
369         {"data", jinfo},
370         {"code", 0},
371     };
372     return return_value.dump();
373 }
374 
handle_get_connections(Server * serv,const std::string & msg)375 static std::string handle_get_connections(Server *serv, const std::string &msg) {
376     json list = json::array();
377     serv->foreach_connection([serv, &list](Connection *conn) {
378         if (serv->is_process_mode() && conn->reactor_id != SwooleTG.id) {
379             return;
380         }
381         if (serv->is_base_mode() && SwooleWG.worker && conn->reactor_id != SwooleWG.worker->id) {
382             return;
383         }
384         list.push_back(get_connection_info(serv, conn));
385     });
386     json return_value{
387         {"data", list},
388         {"code", 0},
389     };
390     return return_value.dump();
391 }
392 
handle_get_connection_info(Server * serv,const std::string & msg)393 static std::string handle_get_connection_info(Server *serv, const std::string &msg) {
394     auto _result = json::parse(msg);
395     if (!_result.is_object() || _result.find("session_id") == _result.end()) {
396         json return_value{
397             {"data", "require parameter session_id"},
398             {"code", 4003},
399         };
400         return return_value.dump();
401     }
402 
403     std::string _session_id = _result["session_id"];
404     int session_id = std::atoi(_session_id.c_str());
405     Connection *conn = serv->get_connection_verify(session_id);
406     if (!conn) {
407         json return_value{
408             {"data", "connection not exists"},
409             {"code", 4004},
410         };
411         return return_value.dump();
412     }
413 
414     json return_value{
415         {"data", get_connection_info(serv, conn)},
416         {"code", 0},
417     };
418     return return_value.dump();
419 }
420 
handle_get_all_ports(Server * serv,const std::string & msg)421 static std::string handle_get_all_ports(Server *serv, const std::string &msg) {
422     json _list = json::array();
423     for (auto port : serv->ports) {
424         json info = json::object({
425             {"host", port->host},
426             {"port", port->port},
427             {"backlog", port->backlog},
428             {"type", port->type},
429             {"ssl", port->ssl},
430             {"protocols", port->get_protocols()},
431             {"connection_num", (long) port->gs->connection_num},
432         });
433         _list.push_back(info);
434     };
435     json return_value{
436         {"data", _list},
437         {"code", 0},
438     };
439     return return_value.dump();
440 }
441 
register_admin_server_commands(Server * serv)442 void register_admin_server_commands(Server *serv) {
443     serv->add_command("get_all_sockets", Server::Command::ALL_PROCESS, handle_get_all_sockets);
444     serv->add_command("get_all_commands", Server::Command::ALL_PROCESS, handle_get_all_commands);
445     serv->add_command("get_socket_info", Server::Command::ALL_PROCESS, handle_get_socket_info);
446     serv->add_command("get_thread_info", Server::Command::ALL_PROCESS, handle_get_thread_info);
447     serv->add_command("get_manager_info", Server::Command::MANAGER, handle_get_manager_info);
448     serv->add_command("get_thread_info", Server::Command::ALL_PROCESS, handle_get_thread_info);
449     serv->add_command("get_memory_info", Server::Command::ALL_PROCESS, handle_get_memory_info);
450     serv->add_command("get_all_unix_sockets", Server::Command::ALL_PROCESS, handle_get_all_unix_sockets);
451     serv->add_command("get_all_ports", Server::Command::MASTER, handle_get_all_ports);
452 
453     int accepted_process_types;
454     if (serv->is_base_mode() || serv->single_thread) {
455         accepted_process_types = Server::Command::EVENT_WORKER | Server::Command::MASTER;
456     } else {
457         accepted_process_types = Server::Command::REACTOR_THREAD;
458     }
459     serv->add_command("get_connections", accepted_process_types, handle_get_connections);
460     serv->add_command("get_connection_info", accepted_process_types, handle_get_connection_info);
461 }
462 }  // namespace swoole
463 
464 typedef std::function<void(zend_object *obj)> objects_store_iterator;
465 
object_valid(zend_object * obj)466 static inline bool object_valid(zend_object *obj) {
467     return obj && IS_OBJ_VALID(obj) && obj->handlers && obj->handlers->get_class_name;
468 }
469 
objects_store_foreach(const objects_store_iterator & fn)470 static void objects_store_foreach(const objects_store_iterator &fn) {
471     for (uint32_t i = 0; i < EG(objects_store).top; i++) {
472         zend_object *obj = EG(objects_store).object_buckets[i];
473         if (object_valid(obj)) {
474             fn(obj);
475         }
476     }
477 }
478 
object_store_count()479 static uint32_t object_store_count() {
480     uint32_t count = 0;
481     objects_store_foreach([&count](zend_object *obj) { count++; });
482     return count;
483 }
484 
ZEND_FUNCTION(swoole_get_vm_status)485 ZEND_FUNCTION(swoole_get_vm_status) {
486     array_init(return_value);
487     add_assoc_long_ex(return_value, ZEND_STRL("object_num"), object_store_count());
488     add_assoc_long_ex(return_value, ZEND_STRL("resource_num"), zend_array_count(&EG(regular_list)));
489 }
490 
ZEND_FUNCTION(swoole_get_objects)491 ZEND_FUNCTION(swoole_get_objects) {
492     zend_objects_store *objects = &EG(objects_store);
493     if (objects->top <= 1) {
494         RETURN_FALSE;
495     }
496 
497     array_init(return_value);
498     objects_store_foreach([return_value](zend_object *obj) {
499         zval zobject;
500         ZVAL_OBJ(&zobject, obj);
501         zval_addref_p(&zobject);
502         add_next_index_zval(return_value, &zobject);
503     });
504 }
505 
ZEND_FUNCTION(swoole_get_object_by_handle)506 ZEND_FUNCTION(swoole_get_object_by_handle) {
507     zend_long handle;
508     ZEND_PARSE_PARAMETERS_START(1, 1)
509     Z_PARAM_LONG(handle)
510     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
511 
512     zend_objects_store *objects = &EG(objects_store);
513     if (objects->top <= 1 || handle >= objects->top) {
514         RETURN_FALSE;
515     }
516 
517     zend_object *obj = objects->object_buckets[handle];
518     if (!object_valid(obj)) {
519         RETURN_FALSE;
520     }
521     GC_ADDREF(obj);
522     RETURN_OBJ(obj);
523 }
524