]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2011 New Dream Network | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
7c673cae FG |
15 | #include "common/admin_socket.h" |
16 | #include "common/admin_socket_client.h" | |
7c673cae | 17 | #include "common/errno.h" |
7c673cae FG |
18 | #include "common/safe_io.h" |
19 | #include "common/version.h" | |
31f18b77 | 20 | #include "include/compat.h" |
7c673cae | 21 | |
7c673cae | 22 | #include <poll.h> |
7c673cae | 23 | #include <sys/un.h> |
7c673cae | 24 | |
31f18b77 FG |
25 | // re-include our assert to clobber the system one; fix dout: |
26 | #include "include/assert.h" | |
91327a77 AA |
27 | #include "include/compat.h" |
28 | #include "include/sock_compat.h" | |
7c673cae FG |
29 | |
30 | #define dout_subsys ceph_subsys_asok | |
31 | #undef dout_prefix | |
32 | #define dout_prefix *_dout << "asok(" << (void*)m_cct << ") " | |
33 | ||
34 | ||
35 | using std::ostringstream; | |
36 | ||
37 | /* | |
38 | * UNIX domain sockets created by an application persist even after that | |
39 | * application closes, unless they're explicitly unlinked. This is because the | |
40 | * directory containing the socket keeps a reference to the socket. | |
41 | * | |
42 | * This code makes things a little nicer by unlinking those dead sockets when | |
43 | * the application exits normally. | |
44 | */ | |
45 | static pthread_mutex_t cleanup_lock = PTHREAD_MUTEX_INITIALIZER; | |
46 | static std::vector <const char*> cleanup_files; | |
47 | static bool cleanup_atexit = false; | |
48 | ||
49 | static void remove_cleanup_file(const char *file) | |
50 | { | |
51 | pthread_mutex_lock(&cleanup_lock); | |
52 | VOID_TEMP_FAILURE_RETRY(unlink(file)); | |
53 | for (std::vector <const char*>::iterator i = cleanup_files.begin(); | |
54 | i != cleanup_files.end(); ++i) { | |
55 | if (strcmp(file, *i) == 0) { | |
56 | free((void*)*i); | |
57 | cleanup_files.erase(i); | |
58 | break; | |
59 | } | |
60 | } | |
61 | pthread_mutex_unlock(&cleanup_lock); | |
62 | } | |
63 | ||
64 | static void remove_all_cleanup_files() | |
65 | { | |
66 | pthread_mutex_lock(&cleanup_lock); | |
67 | for (std::vector <const char*>::iterator i = cleanup_files.begin(); | |
68 | i != cleanup_files.end(); ++i) { | |
69 | VOID_TEMP_FAILURE_RETRY(unlink(*i)); | |
70 | free((void*)*i); | |
71 | } | |
72 | cleanup_files.clear(); | |
73 | pthread_mutex_unlock(&cleanup_lock); | |
74 | } | |
75 | ||
76 | static void add_cleanup_file(const char *file) | |
77 | { | |
78 | char *fname = strdup(file); | |
79 | if (!fname) | |
80 | return; | |
81 | pthread_mutex_lock(&cleanup_lock); | |
82 | cleanup_files.push_back(fname); | |
83 | if (!cleanup_atexit) { | |
84 | atexit(remove_all_cleanup_files); | |
85 | cleanup_atexit = true; | |
86 | } | |
87 | pthread_mutex_unlock(&cleanup_lock); | |
88 | } | |
89 | ||
90 | ||
91 | AdminSocket::AdminSocket(CephContext *cct) | |
92 | : m_cct(cct), | |
93 | m_sock_fd(-1), | |
94 | m_shutdown_rd_fd(-1), | |
95 | m_shutdown_wr_fd(-1), | |
96 | in_hook(false), | |
97 | m_lock("AdminSocket::m_lock"), | |
98 | m_version_hook(NULL), | |
99 | m_help_hook(NULL), | |
100 | m_getdescs_hook(NULL) | |
101 | { | |
102 | } | |
103 | ||
104 | AdminSocket::~AdminSocket() | |
105 | { | |
106 | shutdown(); | |
107 | } | |
108 | ||
109 | /* | |
110 | * This thread listens on the UNIX domain socket for incoming connections. | |
111 | * It only handles one connection at a time at the moment. All I/O is nonblocking, | |
112 | * so that we can implement sensible timeouts. [TODO: make all I/O nonblocking] | |
113 | * | |
114 | * This thread also listens to m_shutdown_rd_fd. If there is any data sent to this | |
115 | * pipe, the thread terminates itself gracefully, allowing the | |
116 | * AdminSocketConfigObs class to join() it. | |
117 | */ | |
118 | ||
119 | #define PFL_SUCCESS ((void*)(intptr_t)0) | |
120 | #define PFL_FAIL ((void*)(intptr_t)1) | |
121 | ||
122 | std::string AdminSocket::create_shutdown_pipe(int *pipe_rd, int *pipe_wr) | |
123 | { | |
124 | int pipefd[2]; | |
91327a77 AA |
125 | if (pipe_cloexec(pipefd) < 0) { |
126 | int e = errno; | |
7c673cae | 127 | ostringstream oss; |
91327a77 | 128 | oss << "AdminSocket::create_shutdown_pipe error: " << cpp_strerror(e); |
7c673cae FG |
129 | return oss.str(); |
130 | } | |
131 | ||
132 | *pipe_rd = pipefd[0]; | |
133 | *pipe_wr = pipefd[1]; | |
134 | return ""; | |
135 | } | |
136 | ||
137 | std::string AdminSocket::destroy_shutdown_pipe() | |
138 | { | |
139 | // Send a byte to the shutdown pipe that the thread is listening to | |
140 | char buf[1] = { 0x0 }; | |
141 | int ret = safe_write(m_shutdown_wr_fd, buf, sizeof(buf)); | |
142 | ||
143 | // Close write end | |
144 | VOID_TEMP_FAILURE_RETRY(close(m_shutdown_wr_fd)); | |
145 | m_shutdown_wr_fd = -1; | |
146 | ||
147 | if (ret != 0) { | |
148 | ostringstream oss; | |
149 | oss << "AdminSocket::destroy_shutdown_pipe error: failed to write" | |
150 | "to thread shutdown pipe: error " << ret; | |
151 | return oss.str(); | |
152 | } | |
153 | ||
154 | join(); | |
155 | ||
156 | // Close read end. Doing this before join() blocks the listenter and prevents | |
157 | // joining. | |
158 | VOID_TEMP_FAILURE_RETRY(close(m_shutdown_rd_fd)); | |
159 | m_shutdown_rd_fd = -1; | |
160 | ||
161 | return ""; | |
162 | } | |
163 | ||
164 | std::string AdminSocket::bind_and_listen(const std::string &sock_path, int *fd) | |
165 | { | |
166 | ldout(m_cct, 5) << "bind_and_listen " << sock_path << dendl; | |
167 | ||
168 | struct sockaddr_un address; | |
169 | if (sock_path.size() > sizeof(address.sun_path) - 1) { | |
170 | ostringstream oss; | |
171 | oss << "AdminSocket::bind_and_listen: " | |
172 | << "The UNIX domain socket path " << sock_path << " is too long! The " | |
173 | << "maximum length on this system is " | |
174 | << (sizeof(address.sun_path) - 1); | |
175 | return oss.str(); | |
176 | } | |
91327a77 | 177 | int sock_fd = socket_cloexec(PF_UNIX, SOCK_STREAM, 0); |
7c673cae FG |
178 | if (sock_fd < 0) { |
179 | int err = errno; | |
180 | ostringstream oss; | |
181 | oss << "AdminSocket::bind_and_listen: " | |
182 | << "failed to create socket: " << cpp_strerror(err); | |
183 | return oss.str(); | |
184 | } | |
7c673cae FG |
185 | memset(&address, 0, sizeof(struct sockaddr_un)); |
186 | address.sun_family = AF_UNIX; | |
187 | snprintf(address.sun_path, sizeof(address.sun_path), | |
188 | "%s", sock_path.c_str()); | |
189 | if (::bind(sock_fd, (struct sockaddr*)&address, | |
190 | sizeof(struct sockaddr_un)) != 0) { | |
191 | int err = errno; | |
192 | if (err == EADDRINUSE) { | |
193 | AdminSocketClient client(sock_path); | |
194 | bool ok; | |
195 | client.ping(&ok); | |
196 | if (ok) { | |
197 | ldout(m_cct, 20) << "socket " << sock_path << " is in use" << dendl; | |
198 | err = EEXIST; | |
199 | } else { | |
200 | ldout(m_cct, 20) << "unlink stale file " << sock_path << dendl; | |
201 | VOID_TEMP_FAILURE_RETRY(unlink(sock_path.c_str())); | |
202 | if (::bind(sock_fd, (struct sockaddr*)&address, | |
203 | sizeof(struct sockaddr_un)) == 0) { | |
204 | err = 0; | |
205 | } else { | |
206 | err = errno; | |
207 | } | |
208 | } | |
209 | } | |
210 | if (err != 0) { | |
211 | ostringstream oss; | |
212 | oss << "AdminSocket::bind_and_listen: " | |
213 | << "failed to bind the UNIX domain socket to '" << sock_path | |
214 | << "': " << cpp_strerror(err); | |
215 | close(sock_fd); | |
216 | return oss.str(); | |
217 | } | |
218 | } | |
219 | if (listen(sock_fd, 5) != 0) { | |
220 | int err = errno; | |
221 | ostringstream oss; | |
222 | oss << "AdminSocket::bind_and_listen: " | |
223 | << "failed to listen to socket: " << cpp_strerror(err); | |
224 | close(sock_fd); | |
225 | VOID_TEMP_FAILURE_RETRY(unlink(sock_path.c_str())); | |
226 | return oss.str(); | |
227 | } | |
228 | *fd = sock_fd; | |
229 | return ""; | |
230 | } | |
231 | ||
232 | void* AdminSocket::entry() | |
233 | { | |
234 | ldout(m_cct, 5) << "entry start" << dendl; | |
235 | while (true) { | |
236 | struct pollfd fds[2]; | |
237 | memset(fds, 0, sizeof(fds)); | |
238 | fds[0].fd = m_sock_fd; | |
239 | fds[0].events = POLLIN | POLLRDBAND; | |
240 | fds[1].fd = m_shutdown_rd_fd; | |
241 | fds[1].events = POLLIN | POLLRDBAND; | |
242 | ||
243 | int ret = poll(fds, 2, -1); | |
244 | if (ret < 0) { | |
245 | int err = errno; | |
246 | if (err == EINTR) { | |
247 | continue; | |
248 | } | |
249 | lderr(m_cct) << "AdminSocket: poll(2) error: '" | |
250 | << cpp_strerror(err) << dendl; | |
251 | return PFL_FAIL; | |
252 | } | |
253 | ||
254 | if (fds[0].revents & POLLIN) { | |
255 | // Send out some data | |
256 | do_accept(); | |
257 | } | |
258 | if (fds[1].revents & POLLIN) { | |
259 | // Parent wants us to shut down | |
260 | return PFL_SUCCESS; | |
261 | } | |
262 | } | |
263 | ldout(m_cct, 5) << "entry exit" << dendl; | |
264 | } | |
265 | ||
266 | void AdminSocket::chown(uid_t uid, gid_t gid) | |
267 | { | |
268 | if (m_sock_fd >= 0) { | |
269 | int r = ::chown(m_path.c_str(), uid, gid); | |
270 | if (r < 0) { | |
271 | r = -errno; | |
272 | lderr(m_cct) << "AdminSocket: failed to chown socket: " | |
273 | << cpp_strerror(r) << dendl; | |
274 | } | |
275 | } | |
276 | } | |
277 | ||
278 | void AdminSocket::chmod(mode_t mode) | |
279 | { | |
280 | if (m_sock_fd >= 0) { | |
281 | int r = ::chmod(m_path.c_str(), mode); | |
282 | if (r < 0) { | |
283 | r = -errno; | |
284 | lderr(m_cct) << "AdminSocket: failed to chmod socket: " | |
285 | << cpp_strerror(r) << dendl; | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | bool AdminSocket::do_accept() | |
291 | { | |
292 | struct sockaddr_un address; | |
293 | socklen_t address_length = sizeof(address); | |
294 | ldout(m_cct, 30) << "AdminSocket: calling accept" << dendl; | |
91327a77 | 295 | int connection_fd = accept_cloexec(m_sock_fd, (struct sockaddr*) &address, |
7c673cae | 296 | &address_length); |
7c673cae FG |
297 | if (connection_fd < 0) { |
298 | int err = errno; | |
299 | lderr(m_cct) << "AdminSocket: do_accept error: '" | |
300 | << cpp_strerror(err) << dendl; | |
301 | return false; | |
302 | } | |
91327a77 | 303 | ldout(m_cct, 30) << "AdminSocket: finished accept" << dendl; |
7c673cae FG |
304 | |
305 | char cmd[1024]; | |
306 | unsigned pos = 0; | |
307 | string c; | |
308 | while (1) { | |
309 | int ret = safe_read(connection_fd, &cmd[pos], 1); | |
310 | if (ret <= 0) { | |
311 | if (ret < 0) { | |
312 | lderr(m_cct) << "AdminSocket: error reading request code: " | |
313 | << cpp_strerror(ret) << dendl; | |
314 | } | |
315 | VOID_TEMP_FAILURE_RETRY(close(connection_fd)); | |
316 | return false; | |
317 | } | |
318 | //ldout(m_cct, 0) << "AdminSocket read byte " << (int)cmd[pos] << " pos " << pos << dendl; | |
319 | if (cmd[0] == '\0') { | |
320 | // old protocol: __be32 | |
321 | if (pos == 3 && cmd[0] == '\0') { | |
322 | switch (cmd[3]) { | |
323 | case 0: | |
324 | c = "0"; | |
325 | break; | |
326 | case 1: | |
327 | c = "perfcounters_dump"; | |
328 | break; | |
329 | case 2: | |
330 | c = "perfcounters_schema"; | |
331 | break; | |
332 | default: | |
333 | c = "foo"; | |
334 | break; | |
335 | } | |
336 | break; | |
337 | } | |
338 | } else { | |
339 | // new protocol: null or \n terminated string | |
340 | if (cmd[pos] == '\n' || cmd[pos] == '\0') { | |
341 | cmd[pos] = '\0'; | |
342 | c = cmd; | |
343 | break; | |
344 | } | |
345 | } | |
346 | if (++pos >= sizeof(cmd)) { | |
347 | lderr(m_cct) << "AdminSocket: error reading request too long" << dendl; | |
348 | VOID_TEMP_FAILURE_RETRY(close(connection_fd)); | |
349 | return false; | |
350 | } | |
351 | } | |
352 | ||
353 | bool rval = false; | |
354 | ||
355 | map<string, cmd_vartype> cmdmap; | |
356 | string format; | |
357 | vector<string> cmdvec; | |
358 | stringstream errss; | |
359 | cmdvec.push_back(cmd); | |
360 | if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) { | |
b32b8144 | 361 | ldout(m_cct, 0) << "AdminSocket: " << errss.str() << dendl; |
7c673cae FG |
362 | VOID_TEMP_FAILURE_RETRY(close(connection_fd)); |
363 | return false; | |
364 | } | |
365 | cmd_getval(m_cct, cmdmap, "format", format); | |
366 | if (format != "json" && format != "json-pretty" && | |
367 | format != "xml" && format != "xml-pretty") | |
368 | format = "json-pretty"; | |
369 | cmd_getval(m_cct, cmdmap, "prefix", c); | |
370 | ||
371 | m_lock.Lock(); | |
372 | map<string,AdminSocketHook*>::iterator p; | |
373 | string match = c; | |
374 | while (match.size()) { | |
375 | p = m_hooks.find(match); | |
376 | if (p != m_hooks.end()) | |
377 | break; | |
378 | ||
379 | // drop right-most word | |
380 | size_t pos = match.rfind(' '); | |
381 | if (pos == std::string::npos) { | |
382 | match.clear(); // we fail | |
383 | break; | |
384 | } else { | |
385 | match.resize(pos); | |
386 | } | |
387 | } | |
388 | ||
389 | bufferlist out; | |
390 | if (p == m_hooks.end()) { | |
391 | lderr(m_cct) << "AdminSocket: request '" << c << "' not defined" << dendl; | |
392 | } else { | |
393 | string args; | |
394 | if (match != c) { | |
395 | args = c.substr(match.length() + 1); | |
396 | } | |
397 | ||
398 | // Drop lock to avoid cycles in cases where the hook takes | |
399 | // the same lock that was held during calls to register/unregister, | |
400 | // and set in_hook to allow unregister to wait for us before | |
401 | // removing this hook. | |
402 | in_hook = true; | |
403 | auto match_hook = p->second; | |
404 | m_lock.Unlock(); | |
405 | bool success = match_hook->call(match, cmdmap, format, out); | |
406 | m_lock.Lock(); | |
407 | in_hook = false; | |
408 | in_hook_cond.Signal(); | |
409 | ||
410 | if (!success) { | |
411 | ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args | |
412 | << "' to " << p->second << " failed" << dendl; | |
413 | out.append("failed"); | |
414 | } else { | |
415 | ldout(m_cct, 5) << "AdminSocket: request '" << match << "' '" << args | |
416 | << "' to " << p->second | |
417 | << " returned " << out.length() << " bytes" << dendl; | |
418 | } | |
419 | uint32_t len = htonl(out.length()); | |
420 | int ret = safe_write(connection_fd, &len, sizeof(len)); | |
421 | if (ret < 0) { | |
422 | lderr(m_cct) << "AdminSocket: error writing response length " | |
423 | << cpp_strerror(ret) << dendl; | |
424 | } else { | |
425 | if (out.write_fd(connection_fd) >= 0) | |
426 | rval = true; | |
427 | } | |
428 | } | |
429 | m_lock.Unlock(); | |
430 | ||
431 | VOID_TEMP_FAILURE_RETRY(close(connection_fd)); | |
432 | return rval; | |
433 | } | |
434 | ||
435 | int AdminSocket::register_command(std::string command, std::string cmddesc, AdminSocketHook *hook, std::string help) | |
436 | { | |
437 | int ret; | |
438 | m_lock.Lock(); | |
439 | if (m_hooks.count(command)) { | |
440 | ldout(m_cct, 5) << "register_command " << command << " hook " << hook << " EEXIST" << dendl; | |
441 | ret = -EEXIST; | |
442 | } else { | |
443 | ldout(m_cct, 5) << "register_command " << command << " hook " << hook << dendl; | |
444 | m_hooks[command] = hook; | |
445 | m_descs[command] = cmddesc; | |
446 | m_help[command] = help; | |
447 | ret = 0; | |
448 | } | |
449 | m_lock.Unlock(); | |
450 | return ret; | |
451 | } | |
452 | ||
453 | int AdminSocket::unregister_command(std::string command) | |
454 | { | |
455 | int ret; | |
456 | m_lock.Lock(); | |
457 | if (m_hooks.count(command)) { | |
458 | ldout(m_cct, 5) << "unregister_command " << command << dendl; | |
459 | m_hooks.erase(command); | |
460 | m_descs.erase(command); | |
461 | m_help.erase(command); | |
462 | ||
463 | // If we are currently processing a command, wait for it to | |
464 | // complete in case it referenced the hook that we are | |
465 | // unregistering. | |
466 | if (in_hook) { | |
467 | in_hook_cond.Wait(m_lock); | |
468 | } | |
469 | ||
470 | ret = 0; | |
471 | } else { | |
472 | ldout(m_cct, 5) << "unregister_command " << command << " ENOENT" << dendl; | |
473 | ret = -ENOENT; | |
474 | } | |
475 | m_lock.Unlock(); | |
476 | return ret; | |
477 | } | |
478 | ||
479 | class VersionHook : public AdminSocketHook { | |
480 | public: | |
481 | bool call(std::string command, cmdmap_t &cmdmap, std::string format, | |
482 | bufferlist& out) override { | |
483 | if (command == "0") { | |
484 | out.append(CEPH_ADMIN_SOCK_VERSION); | |
485 | } else { | |
486 | JSONFormatter jf; | |
487 | jf.open_object_section("version"); | |
31f18b77 | 488 | if (command == "version") { |
7c673cae | 489 | jf.dump_string("version", ceph_version_to_str()); |
31f18b77 FG |
490 | jf.dump_string("release", ceph_release_name(ceph_release())); |
491 | jf.dump_string("release_type", ceph_release_type()); | |
492 | } else if (command == "git_version") { | |
7c673cae | 493 | jf.dump_string("git_version", git_version_to_str()); |
31f18b77 | 494 | } |
7c673cae FG |
495 | ostringstream ss; |
496 | jf.close_section(); | |
497 | jf.flush(ss); | |
498 | out.append(ss.str()); | |
499 | } | |
500 | return true; | |
501 | } | |
502 | }; | |
503 | ||
504 | class HelpHook : public AdminSocketHook { | |
505 | AdminSocket *m_as; | |
506 | public: | |
507 | explicit HelpHook(AdminSocket *as) : m_as(as) {} | |
508 | bool call(string command, cmdmap_t &cmdmap, string format, bufferlist& out) override { | |
509 | Formatter *f = Formatter::create(format, "json-pretty", "json-pretty"); | |
510 | f->open_object_section("help"); | |
511 | for (map<string,string>::iterator p = m_as->m_help.begin(); | |
512 | p != m_as->m_help.end(); | |
513 | ++p) { | |
514 | if (p->second.length()) | |
515 | f->dump_string(p->first.c_str(), p->second); | |
516 | } | |
517 | f->close_section(); | |
518 | ostringstream ss; | |
519 | f->flush(ss); | |
520 | out.append(ss.str()); | |
521 | delete f; | |
522 | return true; | |
523 | } | |
524 | }; | |
525 | ||
526 | class GetdescsHook : public AdminSocketHook { | |
527 | AdminSocket *m_as; | |
528 | public: | |
529 | explicit GetdescsHook(AdminSocket *as) : m_as(as) {} | |
530 | bool call(string command, cmdmap_t &cmdmap, string format, bufferlist& out) override { | |
531 | int cmdnum = 0; | |
181888fb | 532 | JSONFormatter jf; |
7c673cae FG |
533 | jf.open_object_section("command_descriptions"); |
534 | for (map<string,string>::iterator p = m_as->m_descs.begin(); | |
535 | p != m_as->m_descs.end(); | |
536 | ++p) { | |
537 | ostringstream secname; | |
538 | secname << "cmd" << setfill('0') << std::setw(3) << cmdnum; | |
539 | dump_cmd_and_help_to_json(&jf, | |
540 | secname.str().c_str(), | |
541 | p->second.c_str(), | |
542 | m_as->m_help[p->first]); | |
543 | cmdnum++; | |
544 | } | |
545 | jf.close_section(); // command_descriptions | |
181888fb | 546 | jf.enable_line_break(); |
7c673cae FG |
547 | ostringstream ss; |
548 | jf.flush(ss); | |
549 | out.append(ss.str()); | |
550 | return true; | |
551 | } | |
552 | }; | |
553 | ||
554 | bool AdminSocket::init(const std::string &path) | |
555 | { | |
556 | ldout(m_cct, 5) << "init " << path << dendl; | |
557 | ||
558 | /* Set up things for the new thread */ | |
559 | std::string err; | |
560 | int pipe_rd = -1, pipe_wr = -1; | |
561 | err = create_shutdown_pipe(&pipe_rd, &pipe_wr); | |
562 | if (!err.empty()) { | |
563 | lderr(m_cct) << "AdminSocketConfigObs::init: error: " << err << dendl; | |
564 | return false; | |
565 | } | |
566 | int sock_fd; | |
567 | err = bind_and_listen(path, &sock_fd); | |
568 | if (!err.empty()) { | |
569 | lderr(m_cct) << "AdminSocketConfigObs::init: failed: " << err << dendl; | |
570 | close(pipe_rd); | |
571 | close(pipe_wr); | |
572 | return false; | |
573 | } | |
574 | ||
575 | /* Create new thread */ | |
576 | m_sock_fd = sock_fd; | |
577 | m_shutdown_rd_fd = pipe_rd; | |
578 | m_shutdown_wr_fd = pipe_wr; | |
579 | m_path = path; | |
580 | ||
581 | m_version_hook = new VersionHook; | |
582 | register_command("0", "0", m_version_hook, ""); | |
583 | register_command("version", "version", m_version_hook, "get ceph version"); | |
584 | register_command("git_version", "git_version", m_version_hook, "get git sha1"); | |
585 | m_help_hook = new HelpHook(this); | |
586 | register_command("help", "help", m_help_hook, "list available commands"); | |
587 | m_getdescs_hook = new GetdescsHook(this); | |
588 | register_command("get_command_descriptions", "get_command_descriptions", | |
589 | m_getdescs_hook, "list available commands"); | |
590 | ||
591 | create("admin_socket"); | |
592 | add_cleanup_file(m_path.c_str()); | |
593 | return true; | |
594 | } | |
595 | ||
596 | void AdminSocket::shutdown() | |
597 | { | |
598 | std::string err; | |
599 | ||
600 | // Under normal operation this is unlikely to occur. However for some unit | |
601 | // tests, some object members are not initialized and so cannot be deleted | |
602 | // without fault. | |
603 | if (m_shutdown_wr_fd < 0) | |
604 | return; | |
605 | ||
606 | ldout(m_cct, 5) << "shutdown" << dendl; | |
607 | ||
608 | err = destroy_shutdown_pipe(); | |
609 | if (!err.empty()) { | |
610 | lderr(m_cct) << "AdminSocket::shutdown: error: " << err << dendl; | |
611 | } | |
612 | ||
613 | VOID_TEMP_FAILURE_RETRY(close(m_sock_fd)); | |
614 | ||
615 | unregister_command("version"); | |
616 | unregister_command("git_version"); | |
617 | unregister_command("0"); | |
618 | delete m_version_hook; | |
619 | ||
620 | unregister_command("help"); | |
621 | delete m_help_hook; | |
622 | ||
623 | unregister_command("get_command_descriptions"); | |
624 | delete m_getdescs_hook; | |
625 | ||
626 | remove_cleanup_file(m_path.c_str()); | |
627 | m_path.clear(); | |
628 | } |