You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1622 lines
44 KiB

  1. /*
  2. * virt-admin.c: a shell to exercise the libvirt admin API
  3. *
  4. * Copyright (C) 2015 Red Hat, Inc.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library. If not, see
  18. * <http://www.gnu.org/licenses/>.
  19. */
  20. #include <config.h>
  21. #include "virt-admin.h"
  22. #include <getopt.h>
  23. #if WITH_READLINE
  24. # include <readline/readline.h>
  25. # include <readline/history.h>
  26. #endif
  27. #include "internal.h"
  28. #include "viralloc.h"
  29. #include "virerror.h"
  30. #include "virfile.h"
  31. #include "virstring.h"
  32. #include "virthread.h"
  33. #include "virgettext.h"
  34. #include "virtime.h"
  35. #include "virt-admin-completer.h"
  36. #include "vsh-table.h"
  37. #include "virenum.h"
  38. #define VIRT_ADMIN_PROMPT "virt-admin # "
  39. /* we don't need precision to milliseconds in this module */
  40. #define VIRT_ADMIN_TIME_BUFLEN VIR_TIME_STRING_BUFLEN - 3
  41. static char *progname;
  42. static const vshCmdGrp cmdGroups[];
  43. static const vshClientHooks hooks;
  44. VIR_ENUM_DECL(virClientTransport);
  45. VIR_ENUM_IMPL(virClientTransport,
  46. VIR_CLIENT_TRANS_LAST,
  47. N_("unix"),
  48. N_("tcp"),
  49. N_("tls"));
  50. static const char *
  51. vshAdmClientTransportToString(int transport)
  52. {
  53. const char *str = virClientTransportTypeToString(transport);
  54. return str ? _(str) : _("unknown");
  55. }
  56. /*
  57. * vshAdmCatchDisconnect:
  58. *
  59. * We get here when the connection was closed. Unlike virsh, we do not save
  60. * the fact that the event was raised, since there is virAdmConnectIsAlive to
  61. * check if the communication channel has not been closed by remote party.
  62. */
  63. static void
  64. vshAdmCatchDisconnect(virAdmConnectPtr conn G_GNUC_UNUSED,
  65. int reason,
  66. void *opaque)
  67. {
  68. vshControl *ctl = opaque;
  69. const char *str = "unknown reason";
  70. virErrorPtr error;
  71. char *uri = NULL;
  72. if (reason == VIR_CONNECT_CLOSE_REASON_CLIENT)
  73. return;
  74. virErrorPreserveLast(&error);
  75. uri = virAdmConnectGetURI(conn);
  76. switch ((virConnectCloseReason) reason) {
  77. case VIR_CONNECT_CLOSE_REASON_ERROR:
  78. str = N_("Disconnected from %s due to I/O error");
  79. break;
  80. case VIR_CONNECT_CLOSE_REASON_EOF:
  81. str = N_("Disconnected from %s due to end of file");
  82. break;
  83. case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
  84. str = N_("Disconnected from %s due to keepalive timeout");
  85. break;
  86. /* coverity[dead_error_condition] */
  87. case VIR_CONNECT_CLOSE_REASON_CLIENT:
  88. case VIR_CONNECT_CLOSE_REASON_LAST:
  89. break;
  90. }
  91. vshError(ctl, _(str), NULLSTR(uri));
  92. VIR_FREE(uri);
  93. virErrorRestore(&error);
  94. }
  95. static int
  96. vshAdmConnect(vshControl *ctl, unsigned int flags)
  97. {
  98. vshAdmControlPtr priv = ctl->privData;
  99. priv->conn = virAdmConnectOpen(ctl->connname, flags);
  100. if (!priv->conn) {
  101. if (priv->wantReconnect)
  102. vshError(ctl, "%s", _("Failed to reconnect to the admin server"));
  103. else
  104. vshError(ctl, "%s", _("Failed to connect to the admin server"));
  105. return -1;
  106. } else {
  107. if (virAdmConnectRegisterCloseCallback(priv->conn, vshAdmCatchDisconnect,
  108. NULL, NULL) < 0)
  109. vshError(ctl, "%s", _("Unable to register disconnect callback"));
  110. if (priv->wantReconnect)
  111. vshPrint(ctl, "%s\n", _("Reconnected to the admin server"));
  112. }
  113. return 0;
  114. }
  115. static int
  116. vshAdmDisconnect(vshControl *ctl)
  117. {
  118. int ret = 0;
  119. vshAdmControlPtr priv = ctl->privData;
  120. if (!priv->conn)
  121. return ret;
  122. virAdmConnectUnregisterCloseCallback(priv->conn, vshAdmCatchDisconnect);
  123. ret = virAdmConnectClose(priv->conn);
  124. if (ret < 0)
  125. vshError(ctl, "%s", _("Failed to disconnect from the admin server"));
  126. else if (ret > 0)
  127. vshError(ctl, "%s", _("One or more references were leaked after "
  128. "disconnect from the hypervisor"));
  129. priv->conn = NULL;
  130. return ret;
  131. }
  132. /*
  133. * vshAdmReconnect:
  134. *
  135. * Reconnect to a daemon's admin server
  136. *
  137. */
  138. static void
  139. vshAdmReconnect(vshControl *ctl)
  140. {
  141. vshAdmControlPtr priv = ctl->privData;
  142. if (priv->conn)
  143. priv->wantReconnect = true;
  144. vshAdmDisconnect(ctl);
  145. vshAdmConnect(ctl, 0);
  146. priv->wantReconnect = false;
  147. }
  148. /*
  149. * 'uri' command
  150. */
  151. static const vshCmdInfo info_uri[] = {
  152. {.name = "help",
  153. .data = N_("print the admin server URI")
  154. },
  155. {.name = "desc",
  156. .data = ""
  157. },
  158. {.name = NULL}
  159. };
  160. static bool
  161. cmdURI(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
  162. {
  163. char *uri;
  164. vshAdmControlPtr priv = ctl->privData;
  165. uri = virAdmConnectGetURI(priv->conn);
  166. if (!uri) {
  167. vshError(ctl, "%s", _("failed to get URI"));
  168. return false;
  169. }
  170. vshPrint(ctl, "%s\n", uri);
  171. VIR_FREE(uri);
  172. return true;
  173. }
  174. /*
  175. * "version" command
  176. */
  177. static const vshCmdInfo info_version[] = {
  178. {.name = "help",
  179. .data = N_("show version")
  180. },
  181. {.name = "desc",
  182. .data = N_("Display the system and also the daemon version information.")
  183. },
  184. {.name = NULL}
  185. };
  186. static bool
  187. cmdVersion(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
  188. {
  189. unsigned long libVersion;
  190. unsigned long long includeVersion;
  191. unsigned long long daemonVersion;
  192. int ret;
  193. unsigned int major;
  194. unsigned int minor;
  195. unsigned int rel;
  196. vshAdmControlPtr priv = ctl->privData;
  197. includeVersion = LIBVIR_VERSION_NUMBER;
  198. major = includeVersion / 1000000;
  199. includeVersion %= 1000000;
  200. minor = includeVersion / 1000;
  201. rel = includeVersion % 1000;
  202. vshPrint(ctl, _("Compiled against library: libvirt %d.%d.%d\n"),
  203. major, minor, rel);
  204. ret = virGetVersion(&libVersion, NULL, NULL);
  205. if (ret < 0) {
  206. vshError(ctl, "%s", _("failed to get the library version"));
  207. return false;
  208. }
  209. major = libVersion / 1000000;
  210. libVersion %= 1000000;
  211. minor = libVersion / 1000;
  212. rel = libVersion % 1000;
  213. vshPrint(ctl, _("Using library: libvirt %d.%d.%d\n"),
  214. major, minor, rel);
  215. ret = virAdmConnectGetLibVersion(priv->conn, &daemonVersion);
  216. if (ret < 0) {
  217. vshError(ctl, "%s", _("failed to get the daemon version"));
  218. } else {
  219. major = daemonVersion / 1000000;
  220. daemonVersion %= 1000000;
  221. minor = daemonVersion / 1000;
  222. rel = daemonVersion % 1000;
  223. vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"),
  224. major, minor, rel);
  225. }
  226. return true;
  227. }
  228. /* ---------------
  229. * Command Connect
  230. * ---------------
  231. */
  232. static const vshCmdOptDef opts_connect[] = {
  233. {.name = "name",
  234. .type = VSH_OT_STRING,
  235. .flags = VSH_OFLAG_EMPTY_OK,
  236. .help = N_("daemon's admin server connection URI")
  237. },
  238. {.name = NULL}
  239. };
  240. static const vshCmdInfo info_connect[] = {
  241. {.name = "help",
  242. .data = N_("connect to daemon's admin server")
  243. },
  244. {.name = "desc",
  245. .data = N_("Connect to a daemon's administrating server.")
  246. },
  247. {.name = NULL}
  248. };
  249. static bool
  250. cmdConnect(vshControl *ctl, const vshCmd *cmd)
  251. {
  252. const char *name = NULL;
  253. vshAdmControlPtr priv = ctl->privData;
  254. bool connected = priv->conn;
  255. if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0)
  256. return false;
  257. if (name) {
  258. VIR_FREE(ctl->connname);
  259. ctl->connname = g_strdup(name);
  260. }
  261. vshAdmReconnect(ctl);
  262. if (!connected && priv->conn)
  263. vshPrint(ctl, "%s\n", _("Connected to the admin server"));
  264. return !!priv->conn;
  265. }
  266. /* -------------------
  267. * Command server-list
  268. * -------------------
  269. */
  270. static const vshCmdInfo info_srv_list[] = {
  271. {.name = "help",
  272. .data = N_("list available servers on a daemon")
  273. },
  274. {.name = "desc",
  275. .data = N_("List all manageable servers on a daemon.")
  276. },
  277. {.name = NULL}
  278. };
  279. static bool
  280. cmdSrvList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
  281. {
  282. int nsrvs = 0;
  283. size_t i;
  284. bool ret = false;
  285. char *uri = NULL;
  286. virAdmServerPtr *srvs = NULL;
  287. vshAdmControlPtr priv = ctl->privData;
  288. vshTablePtr table = NULL;
  289. /* Obtain a list of available servers on the daemon */
  290. if ((nsrvs = virAdmConnectListServers(priv->conn, &srvs, 0)) < 0) {
  291. uri = virAdmConnectGetURI(priv->conn);
  292. vshError(ctl, _("failed to obtain list of available servers from %s"),
  293. NULLSTR(uri));
  294. goto cleanup;
  295. }
  296. table = vshTableNew(_("Id"), _("Name"), NULL);
  297. if (!table)
  298. goto cleanup;
  299. for (i = 0; i < nsrvs; i++) {
  300. g_autofree char *idStr = NULL;
  301. idStr = g_strdup_printf("%zu", i);
  302. if (vshTableRowAppend(table,
  303. idStr,
  304. virAdmServerGetName(srvs[i]),
  305. NULL) < 0)
  306. goto cleanup;
  307. }
  308. vshTablePrintToStdout(table, ctl);
  309. ret = true;
  310. cleanup:
  311. vshTableFree(table);
  312. if (srvs) {
  313. for (i = 0; i < nsrvs; i++)
  314. virAdmServerFree(srvs[i]);
  315. VIR_FREE(srvs);
  316. }
  317. VIR_FREE(uri);
  318. return ret;
  319. }
  320. /* ------------------------------
  321. * Command server-threadpool-info
  322. * ------------------------------
  323. */
  324. static const vshCmdInfo info_srv_threadpool_info[] = {
  325. {.name = "help",
  326. .data = N_("get server workerpool parameters")
  327. },
  328. {.name = "desc",
  329. .data = N_("Retrieve threadpool attributes from a server. ")
  330. },
  331. {.name = NULL}
  332. };
  333. static const vshCmdOptDef opts_srv_threadpool_info[] = {
  334. {.name = "server",
  335. .type = VSH_OT_DATA,
  336. .flags = VSH_OFLAG_REQ,
  337. .completer = vshAdmServerCompleter,
  338. .help = N_("Server to retrieve threadpool attributes from."),
  339. },
  340. {.name = NULL}
  341. };
  342. static bool
  343. cmdSrvThreadpoolInfo(vshControl *ctl, const vshCmd *cmd)
  344. {
  345. bool ret = false;
  346. virTypedParameterPtr params = NULL;
  347. int nparams = 0;
  348. size_t i;
  349. const char *srvname = NULL;
  350. virAdmServerPtr srv = NULL;
  351. vshAdmControlPtr priv = ctl->privData;
  352. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  353. return false;
  354. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  355. goto cleanup;
  356. if (virAdmServerGetThreadPoolParameters(srv, &params,
  357. &nparams, 0) < 0) {
  358. vshError(ctl, "%s",
  359. _("Unable to get server workerpool parameters"));
  360. goto cleanup;
  361. }
  362. for (i = 0; i < nparams; i++)
  363. vshPrint(ctl, "%-15s: %u\n", params[i].field, params[i].value.ui);
  364. ret = true;
  365. cleanup:
  366. virTypedParamsFree(params, nparams);
  367. if (srv)
  368. virAdmServerFree(srv);
  369. return ret;
  370. }
  371. /* -----------------------------
  372. * Command server-threadpool-set
  373. * -----------------------------
  374. */
  375. static const vshCmdInfo info_srv_threadpool_set[] = {
  376. {.name = "help",
  377. .data = N_("set server workerpool parameters")
  378. },
  379. {.name = "desc",
  380. .data = N_("Tune threadpool attributes on a server. See OPTIONS for "
  381. "currently supported attributes.")
  382. },
  383. {.name = NULL}
  384. };
  385. static const vshCmdOptDef opts_srv_threadpool_set[] = {
  386. {.name = "server",
  387. .type = VSH_OT_DATA,
  388. .flags = VSH_OFLAG_REQ,
  389. .completer = vshAdmServerCompleter,
  390. .help = N_("Server to alter threadpool attributes on."),
  391. },
  392. {.name = "min-workers",
  393. .type = VSH_OT_INT,
  394. .help = N_("Change bottom limit to number of workers."),
  395. },
  396. {.name = "max-workers",
  397. .type = VSH_OT_INT,
  398. .help = N_("Change upper limit to number of workers."),
  399. },
  400. {.name = "priority-workers",
  401. .type = VSH_OT_INT,
  402. .help = N_("Change the current number of priority workers"),
  403. },
  404. {.name = NULL}
  405. };
  406. static bool
  407. cmdSrvThreadpoolSet(vshControl *ctl, const vshCmd *cmd)
  408. {
  409. bool ret = false;
  410. int rv = 0;
  411. unsigned int val, min, max;
  412. int maxparams = 0;
  413. int nparams = 0;
  414. const char *srvname = NULL;
  415. virTypedParameterPtr params = NULL;
  416. virAdmServerPtr srv = NULL;
  417. vshAdmControlPtr priv = ctl->privData;
  418. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  419. return false;
  420. #define PARSE_CMD_TYPED_PARAM(NAME, FIELD) \
  421. if ((rv = vshCommandOptUInt(ctl, cmd, NAME, &val)) < 0) { \
  422. vshError(ctl, _("Unable to parse integer parameter '%s'"), NAME); \
  423. goto cleanup; \
  424. } else if (rv > 0) { \
  425. if (virTypedParamsAddUInt(&params, &nparams, &maxparams, \
  426. FIELD, val) < 0) \
  427. goto save_error; \
  428. }
  429. PARSE_CMD_TYPED_PARAM("max-workers", VIR_THREADPOOL_WORKERS_MAX);
  430. PARSE_CMD_TYPED_PARAM("min-workers", VIR_THREADPOOL_WORKERS_MIN);
  431. PARSE_CMD_TYPED_PARAM("priority-workers", VIR_THREADPOOL_WORKERS_PRIORITY);
  432. #undef PARSE_CMD_TYPED_PARAM
  433. if (!nparams) {
  434. vshError(ctl, "%s",
  435. _("At least one of options --min-workers, --max-workers, "
  436. "--priority-workers is mandatory "));
  437. goto cleanup;
  438. }
  439. if (virTypedParamsGetUInt(params, nparams,
  440. VIR_THREADPOOL_WORKERS_MAX, &max) &&
  441. virTypedParamsGetUInt(params, nparams,
  442. VIR_THREADPOOL_WORKERS_MIN, &min) && min > max) {
  443. vshError(ctl, "%s", _("--min-workers must be less than or equal to "
  444. "--max-workers"));
  445. goto cleanup;
  446. }
  447. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  448. goto cleanup;
  449. if (virAdmServerSetThreadPoolParameters(srv, params,
  450. nparams, 0) < 0)
  451. goto error;
  452. ret = true;
  453. cleanup:
  454. virTypedParamsFree(params, nparams);
  455. if (srv)
  456. virAdmServerFree(srv);
  457. return ret;
  458. save_error:
  459. vshSaveLibvirtError();
  460. error:
  461. vshError(ctl, "%s", _("Unable to change server workerpool parameters"));
  462. goto cleanup;
  463. }
  464. /* ---------------------------
  465. * Command server-clients-list
  466. * ---------------------------
  467. */
  468. static const vshCmdInfo info_srv_clients_list[] = {
  469. {.name = "help",
  470. .data = N_("list clients connected to <server>")
  471. },
  472. {.name = "desc",
  473. .data = N_("List all manageable clients connected to <server>.")
  474. },
  475. {.name = NULL}
  476. };
  477. static const vshCmdOptDef opts_srv_clients_list[] = {
  478. {.name = "server",
  479. .type = VSH_OT_DATA,
  480. .flags = VSH_OFLAG_REQ,
  481. .completer = vshAdmServerCompleter,
  482. .help = N_("server which to list connected clients from"),
  483. },
  484. {.name = NULL}
  485. };
  486. static bool
  487. cmdSrvClientsList(vshControl *ctl, const vshCmd *cmd)
  488. {
  489. int nclts = 0;
  490. size_t i;
  491. bool ret = false;
  492. const char *srvname = NULL;
  493. unsigned long long id;
  494. virClientTransport transport;
  495. virAdmServerPtr srv = NULL;
  496. virAdmClientPtr *clts = NULL;
  497. vshAdmControlPtr priv = ctl->privData;
  498. vshTablePtr table = NULL;
  499. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  500. return false;
  501. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  502. goto cleanup;
  503. /* Obtain a list of clients connected to server @srv */
  504. if ((nclts = virAdmServerListClients(srv, &clts, 0)) < 0) {
  505. vshError(ctl, _("failed to obtain list of connected clients "
  506. "from server '%s'"), virAdmServerGetName(srv));
  507. goto cleanup;
  508. }
  509. table = vshTableNew(_("Id"), _("Transport"), _("Connected since"), NULL);
  510. if (!table)
  511. goto cleanup;
  512. for (i = 0; i < nclts; i++) {
  513. g_autoptr(GDateTime) then = NULL;
  514. g_autofree gchar *thenstr = NULL;
  515. g_autofree char *idStr = NULL;
  516. virAdmClientPtr client = clts[i];
  517. id = virAdmClientGetID(client);
  518. then = g_date_time_new_from_unix_local(virAdmClientGetTimestamp(client));
  519. transport = virAdmClientGetTransport(client);
  520. thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S%z");
  521. idStr = g_strdup_printf("%llu", id);
  522. if (vshTableRowAppend(table, idStr,
  523. vshAdmClientTransportToString(transport),
  524. thenstr, NULL) < 0)
  525. goto cleanup;
  526. }
  527. vshTablePrintToStdout(table, ctl);
  528. ret = true;
  529. cleanup:
  530. vshTableFree(table);
  531. if (clts) {
  532. for (i = 0; i < nclts; i++)
  533. virAdmClientFree(clts[i]);
  534. VIR_FREE(clts);
  535. }
  536. virAdmServerFree(srv);
  537. return ret;
  538. }
  539. /* -------------------
  540. * Command client-info
  541. * -------------------
  542. */
  543. static const vshCmdInfo info_client_info[] = {
  544. {.name = "help",
  545. .data = N_("retrieve client's identity info from server")
  546. },
  547. {.name = "desc",
  548. .data = N_("Retrieve identity details about <client> from <server>")
  549. },
  550. {.name = NULL}
  551. };
  552. static const vshCmdOptDef opts_client_info[] = {
  553. {.name = "server",
  554. .type = VSH_OT_DATA,
  555. .flags = VSH_OFLAG_REQ,
  556. .completer = vshAdmServerCompleter,
  557. .help = N_("server to which <client> is connected to"),
  558. },
  559. {.name = "client",
  560. .type = VSH_OT_DATA,
  561. .flags = VSH_OFLAG_REQ,
  562. .help = N_("client which to retrieve identity information for"),
  563. },
  564. {.name = NULL}
  565. };
  566. static bool
  567. cmdClientInfo(vshControl *ctl, const vshCmd *cmd)
  568. {
  569. bool ret = false;
  570. size_t i;
  571. unsigned long long id;
  572. const char *srvname = NULL;
  573. g_autoptr(GDateTime) then = NULL;
  574. g_autofree gchar *thenstr = NULL;
  575. virAdmServerPtr srv = NULL;
  576. virAdmClientPtr clnt = NULL;
  577. virTypedParameterPtr params = NULL;
  578. int nparams = 0;
  579. vshAdmControlPtr priv = ctl->privData;
  580. if (vshCommandOptULongLong(ctl, cmd, "client", &id) < 0)
  581. return false;
  582. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  583. return false;
  584. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)) ||
  585. !(clnt = virAdmServerLookupClient(srv, id, 0)))
  586. goto cleanup;
  587. /* Retrieve client identity info */
  588. if (virAdmClientGetInfo(clnt, &params, &nparams, 0) < 0) {
  589. vshError(ctl, _("failed to retrieve client identity information for "
  590. "client '%llu' connected to server '%s'"),
  591. id, virAdmServerGetName(srv));
  592. goto cleanup;
  593. }
  594. then = g_date_time_new_from_unix_local(virAdmClientGetTimestamp(clnt));
  595. thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S%z");
  596. /* this info is provided by the client object itself */
  597. vshPrint(ctl, "%-15s: %llu\n", "id", virAdmClientGetID(clnt));
  598. vshPrint(ctl, "%-15s: %s\n", "connection_time", thenstr);
  599. vshPrint(ctl, "%-15s: %s\n", "transport",
  600. vshAdmClientTransportToString(virAdmClientGetTransport(clnt)));
  601. for (i = 0; i < nparams; i++) {
  602. char *str = vshGetTypedParamValue(ctl, &params[i]);
  603. vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
  604. VIR_FREE(str);
  605. }
  606. ret = true;
  607. cleanup:
  608. virTypedParamsFree(params, nparams);
  609. virAdmServerFree(srv);
  610. virAdmClientFree(clnt);
  611. return ret;
  612. }
  613. /* -------------------------
  614. * Command client-disconnect
  615. * -------------------------
  616. */
  617. static const vshCmdInfo info_client_disconnect[] = {
  618. {.name = "help",
  619. .data = N_("force disconnect a client from the given server")
  620. },
  621. {.name = "desc",
  622. .data = N_("Force close a specific client's connection to the given "
  623. "server.")
  624. },
  625. {.name = NULL}
  626. };
  627. static const vshCmdOptDef opts_client_disconnect[] = {
  628. {.name = "server",
  629. .type = VSH_OT_DATA,
  630. .flags = VSH_OFLAG_REQ,
  631. .completer = vshAdmServerCompleter,
  632. .help = N_("server which the client is currently connected to"),
  633. },
  634. {.name = "client",
  635. .type = VSH_OT_INT,
  636. .flags = VSH_OFLAG_REQ,
  637. .help = N_("client which to disconnect, specified by ID"),
  638. },
  639. {.name = NULL}
  640. };
  641. static bool
  642. cmdClientDisconnect(vshControl *ctl, const vshCmd *cmd)
  643. {
  644. bool ret = false;
  645. const char *srvname = NULL;
  646. unsigned long long id = 0;
  647. virAdmServerPtr srv = NULL;
  648. virAdmClientPtr client = NULL;
  649. vshAdmControlPtr priv = ctl->privData;
  650. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  651. return false;
  652. if (vshCommandOptULongLongWrap(ctl, cmd, "client", &id) < 0)
  653. return false;
  654. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  655. goto cleanup;
  656. if (!(client = virAdmServerLookupClient(srv, id, 0)))
  657. goto cleanup;
  658. if (virAdmClientClose(client, 0) < 0) {
  659. vshError(ctl, _("Failed to disconnect client '%llu' from server %s"),
  660. id, virAdmServerGetName(srv));
  661. goto cleanup;
  662. }
  663. vshPrint(ctl, _("Client '%llu' disconnected"), id);
  664. ret = true;
  665. cleanup:
  666. virAdmClientFree(client);
  667. virAdmServerFree(srv);
  668. return ret;
  669. }
  670. /* ---------------------------
  671. * Command server-clients-info
  672. * ---------------------------
  673. */
  674. static const vshCmdInfo info_srv_clients_info[] = {
  675. {.name = "help",
  676. .data = N_("get server's client-related configuration limits")
  677. },
  678. {.name = "desc",
  679. .data = N_("Retrieve server's client-related configuration limits ")
  680. },
  681. {.name = NULL}
  682. };
  683. static const vshCmdOptDef opts_srv_clients_info[] = {
  684. {.name = "server",
  685. .type = VSH_OT_DATA,
  686. .flags = VSH_OFLAG_REQ,
  687. .completer = vshAdmServerCompleter,
  688. .help = N_("Server to retrieve the client limits from."),
  689. },
  690. {.name = NULL}
  691. };
  692. static bool
  693. cmdSrvClientsInfo(vshControl *ctl, const vshCmd *cmd)
  694. {
  695. bool ret = false;
  696. virTypedParameterPtr params = NULL;
  697. int nparams = 0;
  698. size_t i;
  699. const char *srvname = NULL;
  700. virAdmServerPtr srv = NULL;
  701. vshAdmControlPtr priv = ctl->privData;
  702. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  703. return false;
  704. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  705. goto cleanup;
  706. if (virAdmServerGetClientLimits(srv, &params, &nparams, 0) < 0) {
  707. vshError(ctl, "%s", _("Unable to retrieve client limits "
  708. "from server's configuration"));
  709. goto cleanup;
  710. }
  711. for (i = 0; i < nparams; i++)
  712. vshPrint(ctl, "%-20s: %u\n", params[i].field, params[i].value.ui);
  713. ret = true;
  714. cleanup:
  715. virTypedParamsFree(params, nparams);
  716. virAdmServerFree(srv);
  717. return ret;
  718. }
  719. /* --------------------------
  720. * Command server-clients-set
  721. * --------------------------
  722. */
  723. static const vshCmdInfo info_srv_clients_set[] = {
  724. {.name = "help",
  725. .data = N_("set server's client-related configuration limits")
  726. },
  727. {.name = "desc",
  728. .data = N_("Tune server's client-related configuration limits. "
  729. "See OPTIONS for currently supported attributes.")
  730. },
  731. {.name = NULL}
  732. };
  733. static const vshCmdOptDef opts_srv_clients_set[] = {
  734. {.name = "server",
  735. .type = VSH_OT_DATA,
  736. .flags = VSH_OFLAG_REQ,
  737. .completer = vshAdmServerCompleter,
  738. .help = N_("Server to alter the client-related configuration limits on."),
  739. },
  740. {.name = "max-clients",
  741. .type = VSH_OT_INT,
  742. .help = N_("Change the upper limit to overall number of clients "
  743. "connected to the server."),
  744. },
  745. {.name = "max-unauth-clients",
  746. .type = VSH_OT_INT,
  747. .help = N_("Change the upper limit to number of clients waiting for "
  748. "authentication to be connected to the server"),
  749. },
  750. {.name = NULL}
  751. };
  752. static bool
  753. cmdSrvClientsSet(vshControl *ctl, const vshCmd *cmd)
  754. {
  755. bool ret = false;
  756. int rv = 0;
  757. unsigned int val, max, unauth_max;
  758. int maxparams = 0;
  759. int nparams = 0;
  760. const char *srvname = NULL;
  761. virAdmServerPtr srv = NULL;
  762. virTypedParameterPtr params = NULL;
  763. vshAdmControlPtr priv = ctl->privData;
  764. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  765. return false;
  766. #define PARSE_CMD_TYPED_PARAM(NAME, FIELD) \
  767. if ((rv = vshCommandOptUInt(ctl, cmd, NAME, &val)) < 0) { \
  768. vshError(ctl, _("Unable to parse integer parameter '%s'"), NAME); \
  769. goto cleanup; \
  770. } else if (rv > 0) { \
  771. if (virTypedParamsAddUInt(&params, &nparams, &maxparams, \
  772. FIELD, val) < 0) \
  773. goto save_error; \
  774. }
  775. PARSE_CMD_TYPED_PARAM("max-clients", VIR_SERVER_CLIENTS_MAX);
  776. PARSE_CMD_TYPED_PARAM("max-unauth-clients", VIR_SERVER_CLIENTS_UNAUTH_MAX);
  777. #undef PARSE_CMD_TYPED_PARAM
  778. if (!nparams) {
  779. vshError(ctl, "%s", _("At least one of options --max-clients, "
  780. "--max-unauth-clients is mandatory"));
  781. goto cleanup;
  782. }
  783. if (virTypedParamsGetUInt(params, nparams,
  784. VIR_SERVER_CLIENTS_MAX, &max) &&
  785. virTypedParamsGetUInt(params, nparams,
  786. VIR_SERVER_CLIENTS_UNAUTH_MAX, &unauth_max) &&
  787. unauth_max > max) {
  788. vshError(ctl, "%s", _("--max-unauth-clients must be less than or equal to "
  789. "--max-clients"));
  790. goto cleanup;
  791. }
  792. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  793. goto cleanup;
  794. if (virAdmServerSetClientLimits(srv, params, nparams, 0) < 0)
  795. goto error;
  796. ret = true;
  797. cleanup:
  798. virTypedParamsFree(params, nparams);
  799. virAdmServerFree(srv);
  800. return ret;
  801. save_error:
  802. vshSaveLibvirtError();
  803. error:
  804. vshError(ctl, "%s", _("Unable to change server's client-related "
  805. "configuration limits"));
  806. goto cleanup;
  807. }
  808. /* --------------------------
  809. * Command server-update-tls
  810. * --------------------------
  811. */
  812. static const vshCmdInfo info_srv_update_tls_file[] = {
  813. {.name = "help",
  814. .data = N_("notify server to update TLS related files online.")
  815. },
  816. {.name = "desc",
  817. .data = N_("notify server to update the CA cert, "
  818. "CA CRL, server cert / key without restarts. "
  819. "See OPTIONS for currently supported attributes.")
  820. },
  821. {.name = NULL}
  822. };
  823. static const vshCmdOptDef opts_srv_update_tls_file[] = {
  824. {.name = "server",
  825. .type = VSH_OT_DATA,
  826. .flags = VSH_OFLAG_REQ,
  827. .help = N_("Available servers on a daemon. "
  828. "Currently only supports 'libvirtd' or 'virtproxyd'.")
  829. },
  830. {.name = NULL}
  831. };
  832. static bool
  833. cmdSrvUpdateTlsFiles(vshControl *ctl, const vshCmd *cmd)
  834. {
  835. bool ret = false;
  836. const char *srvname = NULL;
  837. virAdmServerPtr srv = NULL;
  838. vshAdmControlPtr priv = ctl->privData;
  839. if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0)
  840. return false;
  841. if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0)))
  842. goto cleanup;
  843. if (virAdmServerUpdateTlsFiles(srv, 0) < 0) {
  844. vshError(ctl, "%s", _("Unable to update server's tls related files."));
  845. goto cleanup;
  846. }
  847. ret = true;
  848. vshPrint(ctl, "update tls related files succeed\n");
  849. cleanup:
  850. virAdmServerFree(srv);
  851. return ret;
  852. }
  853. /* --------------------------
  854. * Command daemon-log-filters
  855. * --------------------------
  856. */
  857. static const vshCmdInfo info_daemon_log_filters[] = {
  858. {.name = "help",
  859. .data = N_("fetch or set the currently defined set of logging filters on "
  860. "daemon")
  861. },
  862. {.name = "desc",
  863. .data = N_("Depending on whether run with or without options, the command "
  864. "fetches or redefines the existing active set of filters on "
  865. "daemon.")
  866. },
  867. {.name = NULL}
  868. };
  869. static const vshCmdOptDef opts_daemon_log_filters[] = {
  870. {.name = "filters",
  871. .type = VSH_OT_STRING,
  872. .help = N_("redefine the existing set of logging filters"),
  873. .flags = VSH_OFLAG_EMPTY_OK
  874. },
  875. {.name = NULL}
  876. };
  877. static bool
  878. cmdDaemonLogFilters(vshControl *ctl, const vshCmd *cmd)
  879. {
  880. int nfilters;
  881. char *filters = NULL;
  882. vshAdmControlPtr priv = ctl->privData;
  883. if (vshCommandOptBool(cmd, "filters")) {
  884. if ((vshCommandOptStringReq(ctl, cmd, "filters",
  885. (const char **) &filters) < 0 ||
  886. virAdmConnectSetLoggingFilters(priv->conn, filters, 0) < 0)) {
  887. vshError(ctl, _("Unable to change daemon logging settings"));
  888. return false;
  889. }
  890. } else {
  891. if ((nfilters = virAdmConnectGetLoggingFilters(priv->conn,
  892. &filters, 0)) < 0) {
  893. vshError(ctl, _("Unable to get daemon logging filters information"));
  894. return false;
  895. }
  896. vshPrintExtra(ctl, " %-15s", _("Logging filters: "));
  897. vshPrint(ctl, "%s\n", NULLSTR_EMPTY(filters));
  898. }
  899. return true;
  900. }
  901. /* --------------------------
  902. * Command daemon-log-outputs
  903. * --------------------------
  904. */
  905. static const vshCmdInfo info_daemon_log_outputs[] = {
  906. {.name = "help",
  907. .data = N_("fetch or set the currently defined set of logging outputs on "
  908. "daemon")
  909. },
  910. {.name = "desc",
  911. .data = N_("Depending on whether run with or without options, the command "
  912. "fetches or redefines the existing active set of outputs on "
  913. "daemon.")
  914. },
  915. {.name = NULL}
  916. };
  917. static const vshCmdOptDef opts_daemon_log_outputs[] = {
  918. {.name = "outputs",
  919. .type = VSH_OT_STRING,
  920. .help = N_("redefine the existing set of logging outputs"),
  921. .flags = VSH_OFLAG_EMPTY_OK
  922. },
  923. {.name = NULL}
  924. };
  925. static bool
  926. cmdDaemonLogOutputs(vshControl *ctl, const vshCmd *cmd)
  927. {
  928. int noutputs;
  929. char *outputs = NULL;
  930. vshAdmControlPtr priv = ctl->privData;
  931. if (vshCommandOptBool(cmd, "outputs")) {
  932. if ((vshCommandOptStringReq(ctl, cmd, "outputs",
  933. (const char **) &outputs) < 0 ||
  934. virAdmConnectSetLoggingOutputs(priv->conn, outputs, 0) < 0)) {
  935. vshError(ctl, _("Unable to change daemon logging settings"));
  936. return false;
  937. }
  938. } else {
  939. if ((noutputs = virAdmConnectGetLoggingOutputs(priv->conn,
  940. &outputs, 0)) < 0) {
  941. vshError(ctl, _("Unable to get daemon logging outputs information"));
  942. return false;
  943. }
  944. vshPrintExtra(ctl, " %-15s", _("Logging outputs: "));
  945. vshPrint(ctl, "%s\n", NULLSTR_EMPTY(outputs));
  946. }
  947. return true;
  948. }
  949. static void *
  950. vshAdmConnectionHandler(vshControl *ctl)
  951. {
  952. vshAdmControlPtr priv = ctl->privData;
  953. if (!virAdmConnectIsAlive(priv->conn))
  954. vshAdmReconnect(ctl);
  955. if (!virAdmConnectIsAlive(priv->conn)) {
  956. vshError(ctl, "%s", _("no valid connection"));
  957. return NULL;
  958. }
  959. return priv->conn;
  960. }
  961. /*
  962. * Initialize connection.
  963. */
  964. static bool
  965. vshAdmInit(vshControl *ctl)
  966. {
  967. vshAdmControlPtr priv = ctl->privData;
  968. /* Since we have the commandline arguments parsed, we need to
  969. * reload our initial settings to make debugging and readline
  970. * work properly */
  971. vshInitReload(ctl);
  972. if (priv->conn)
  973. return false;
  974. /* set up the library error handler */
  975. virSetErrorFunc(NULL, vshErrorHandler);
  976. if (virEventRegisterDefaultImpl() < 0)
  977. return false;
  978. if (virThreadCreate(&ctl->eventLoop, true, vshEventLoop, ctl) < 0)
  979. return false;
  980. ctl->eventLoopStarted = true;
  981. if (ctl->connname) {
  982. vshAdmReconnect(ctl);
  983. /* Connecting to a named connection must succeed, but we delay
  984. * connecting to the default connection until we need it
  985. * (since the first command might be 'connect' which allows a
  986. * non-default connection, or might be 'help' which needs no
  987. * connection).
  988. */
  989. if (!priv->conn) {
  990. vshReportError(ctl);
  991. return false;
  992. }
  993. }
  994. return true;
  995. }
  996. static void
  997. vshAdmDeinitTimer(int timer G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED)
  998. {
  999. /* nothing to be done here */
  1000. }
  1001. /*
  1002. * Deinitialize virt-admin
  1003. */
  1004. static void
  1005. vshAdmDeinit(vshControl *ctl)
  1006. {
  1007. vshAdmControlPtr priv = ctl->privData;
  1008. vshDeinit(ctl);
  1009. VIR_FREE(ctl->connname);
  1010. if (priv->conn)
  1011. vshAdmDisconnect(ctl);
  1012. virResetLastError();
  1013. if (ctl->eventLoopStarted) {
  1014. int timer;
  1015. virMutexLock(&ctl->lock);
  1016. ctl->quit = true;
  1017. /* HACK: Add a dummy timeout to break event loop */
  1018. timer = virEventAddTimeout(0, vshAdmDeinitTimer, NULL, NULL);
  1019. virMutexUnlock(&ctl->lock);
  1020. virThreadJoin(&ctl->eventLoop);
  1021. if (timer != -1)
  1022. virEventRemoveTimeout(timer);
  1023. ctl->eventLoopStarted = false;
  1024. }
  1025. virMutexDestroy(&ctl->lock);
  1026. }
  1027. /*
  1028. * Print usage
  1029. */
  1030. static void
  1031. vshAdmUsage(void)
  1032. {
  1033. const vshCmdGrp *grp;
  1034. const vshCmdDef *cmd;
  1035. fprintf(stdout, _("\n%s [options]... [<command_string>]"
  1036. "\n%s [options]... <command> [args...]\n\n"
  1037. " options:\n"
  1038. " -c | --connect=URI daemon admin connection URI\n"
  1039. " -d | --debug=NUM debug level [0-4]\n"
  1040. " -h | --help this help\n"
  1041. " -l | --log=FILE output logging to file\n"
  1042. " -q | --quiet quiet mode\n"
  1043. " -v short version\n"
  1044. " -V long version\n"
  1045. " --version[=TYPE] version, TYPE is short or long (default short)\n"
  1046. " commands (non interactive mode):\n\n"), progname,
  1047. progname);
  1048. for (grp = cmdGroups; grp->name; grp++) {
  1049. fprintf(stdout, _(" %s (help keyword '%s')\n"),
  1050. grp->name, grp->keyword);
  1051. for (cmd = grp->commands; cmd->name; cmd++) {
  1052. if (cmd->flags & VSH_CMD_FLAG_ALIAS)
  1053. continue;
  1054. fprintf(stdout,
  1055. " %-30s %s\n", cmd->name,
  1056. _(vshCmddefGetInfo(cmd, "help")));
  1057. }
  1058. fprintf(stdout, "\n");
  1059. }
  1060. fprintf(stdout, "%s",
  1061. _("\n (specify help <group> for details about the commands in the group)\n"));
  1062. fprintf(stdout, "%s",
  1063. _("\n (specify help <command> for details about the command)\n\n"));
  1064. return;
  1065. }
  1066. /*
  1067. * Show version and options compiled in
  1068. */
  1069. static void
  1070. vshAdmShowVersion(vshControl *ctl G_GNUC_UNUSED)
  1071. {
  1072. /* FIXME - list a copyright blurb, as in GNU programs? */
  1073. vshPrint(ctl, _("Virt-admin command line tool of libvirt %s\n"), VERSION);
  1074. vshPrint(ctl, _("See web site at %s\n\n"), "https://libvirt.org/");
  1075. vshPrint(ctl, "%s", _("Compiled with support for:"));
  1076. #ifdef WITH_LIBVIRTD
  1077. vshPrint(ctl, " Daemon");
  1078. #endif
  1079. #ifdef ENABLE_DEBUG
  1080. vshPrint(ctl, " Debug");
  1081. #endif
  1082. #if WITH_READLINE
  1083. vshPrint(ctl, " Readline");
  1084. #endif
  1085. vshPrint(ctl, "\n");
  1086. }
  1087. static bool
  1088. vshAdmParseArgv(vshControl *ctl, int argc, char **argv)
  1089. {
  1090. int arg, debug;
  1091. size_t i;
  1092. int longindex = -1;
  1093. struct option opt[] = {
  1094. {"connect", required_argument, NULL, 'c'},
  1095. {"debug", required_argument, NULL, 'd'},
  1096. {"help", no_argument, NULL, 'h'},
  1097. {"log", required_argument, NULL, 'l'},
  1098. {"quiet", no_argument, NULL, 'q'},
  1099. {"version", optional_argument, NULL, 'v'},
  1100. {NULL, 0, NULL, 0}
  1101. };
  1102. /* Standard (non-command) options. The leading + ensures that no
  1103. * argument reordering takes place, so that command options are
  1104. * not confused with top-level virt-admin options. */
  1105. while ((arg = getopt_long(argc, argv, "+:c:d:hl:qvV", opt, &longindex)) != -1) {
  1106. switch (arg) {
  1107. case 'c':
  1108. VIR_FREE(ctl->connname);
  1109. ctl->connname = g_strdup(optarg);
  1110. break;
  1111. case 'd':
  1112. if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) {
  1113. vshError(ctl, _("option %s takes a numeric argument"),
  1114. longindex == -1 ? "-d" : "--debug");
  1115. exit(EXIT_FAILURE);
  1116. }
  1117. if (debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR)
  1118. vshError(ctl, _("ignoring debug level %d out of range [%d-%d]"),
  1119. debug, VSH_ERR_DEBUG, VSH_ERR_ERROR);
  1120. else
  1121. ctl->debug = debug;
  1122. break;
  1123. case 'h':
  1124. vshAdmUsage();
  1125. exit(EXIT_SUCCESS);
  1126. break;
  1127. case 'l':
  1128. vshCloseLogFile(ctl);
  1129. ctl->logfile = g_strdup(optarg);
  1130. vshOpenLogFile(ctl);
  1131. break;
  1132. case 'q':
  1133. ctl->quiet = true;
  1134. break;
  1135. case 'v':
  1136. if (STRNEQ_NULLABLE(optarg, "long")) {
  1137. puts(VERSION);
  1138. exit(EXIT_SUCCESS);
  1139. }
  1140. G_GNUC_FALLTHROUGH;
  1141. case 'V':
  1142. vshAdmShowVersion(ctl);
  1143. exit(EXIT_SUCCESS);
  1144. case ':':
  1145. for (i = 0; opt[i].name != NULL; i++) {
  1146. if (opt[i].val == optopt)
  1147. break;
  1148. }
  1149. if (opt[i].name)
  1150. vshError(ctl, _("option '-%c'/'--%s' requires an argument"),
  1151. optopt, opt[i].name);
  1152. else
  1153. vshError(ctl, _("option '-%c' requires an argument"), optopt);
  1154. exit(EXIT_FAILURE);
  1155. case '?':
  1156. if (optopt)
  1157. vshError(ctl, _("unsupported option '-%c'. See --help."), optopt);
  1158. else
  1159. vshError(ctl, _("unsupported option '%s'. See --help."), argv[optind - 1]);
  1160. exit(EXIT_FAILURE);
  1161. default:
  1162. vshError(ctl, _("unknown option"));
  1163. exit(EXIT_FAILURE);
  1164. }
  1165. longindex = -1;
  1166. }
  1167. if (argc == optind) {
  1168. ctl->imode = true;
  1169. } else {
  1170. /* parse command */
  1171. ctl->imode = false;
  1172. if (argc - optind == 1) {
  1173. vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]);
  1174. return vshCommandStringParse(ctl, argv[optind], NULL);
  1175. } else {
  1176. return vshCommandArgvParse(ctl, argc - optind, argv + optind);
  1177. }
  1178. }
  1179. return true;
  1180. }
  1181. static const vshCmdDef vshAdmCmds[] = {
  1182. VSH_CMD_CD,
  1183. VSH_CMD_ECHO,
  1184. VSH_CMD_EXIT,
  1185. VSH_CMD_HELP,
  1186. VSH_CMD_PWD,
  1187. VSH_CMD_QUIT,
  1188. VSH_CMD_SELF_TEST,
  1189. VSH_CMD_COMPLETE,
  1190. {.name = "uri",
  1191. .handler = cmdURI,
  1192. .opts = NULL,
  1193. .info = info_uri,
  1194. .flags = 0
  1195. },
  1196. {.name = "version",
  1197. .handler = cmdVersion,
  1198. .opts = NULL,
  1199. .info = info_version,
  1200. .flags = 0
  1201. },
  1202. {.name = "connect",
  1203. .handler = cmdConnect,
  1204. .opts = opts_connect,
  1205. .info = info_connect,
  1206. .flags = VSH_CMD_FLAG_NOCONNECT
  1207. },
  1208. {.name = NULL}
  1209. };
  1210. static const vshCmdDef monitoringCmds[] = {
  1211. {.name = "srv-list",
  1212. .flags = VSH_CMD_FLAG_ALIAS,
  1213. .alias = "server-list"
  1214. },
  1215. {.name = "server-list",
  1216. .handler = cmdSrvList,
  1217. .opts = NULL,
  1218. .info = info_srv_list,
  1219. .flags = 0
  1220. },
  1221. {.name = "srv-threadpool-info",
  1222. .flags = VSH_CMD_FLAG_ALIAS,
  1223. .alias = "server-threadpool-info"
  1224. },
  1225. {.name = "server-threadpool-info",
  1226. .handler = cmdSrvThreadpoolInfo,
  1227. .opts = opts_srv_threadpool_info,
  1228. .info = info_srv_threadpool_info,
  1229. .flags = 0
  1230. },
  1231. {.name = "srv-clients-list",
  1232. .flags = VSH_CMD_FLAG_ALIAS,
  1233. .alias = "client-list"
  1234. },
  1235. {.name = "client-list",
  1236. .handler = cmdSrvClientsList,
  1237. .opts = opts_srv_clients_list,
  1238. .info = info_srv_clients_list,
  1239. .flags = 0
  1240. },
  1241. {.name = "client-info",
  1242. .handler = cmdClientInfo,
  1243. .opts = opts_client_info,
  1244. .info = info_client_info,
  1245. .flags = 0
  1246. },
  1247. {.name = "srv-clients-info",
  1248. .flags = VSH_CMD_FLAG_ALIAS,
  1249. .alias = "server-clients-info"
  1250. },
  1251. {.name = "server-clients-info",
  1252. .handler = cmdSrvClientsInfo,
  1253. .opts = opts_srv_clients_info,
  1254. .info = info_srv_clients_info,
  1255. .flags = 0
  1256. },
  1257. {.name = NULL}
  1258. };
  1259. static const vshCmdDef managementCmds[] = {
  1260. {.name = "srv-threadpool-set",
  1261. .flags = VSH_CMD_FLAG_ALIAS,
  1262. .alias = "server-threadpool-set"
  1263. },
  1264. {.name = "server-threadpool-set",
  1265. .handler = cmdSrvThreadpoolSet,
  1266. .opts = opts_srv_threadpool_set,
  1267. .info = info_srv_threadpool_set,
  1268. .flags = 0
  1269. },
  1270. {.name = "client-disconnect",
  1271. .handler = cmdClientDisconnect,
  1272. .opts = opts_client_disconnect,
  1273. .info = info_client_disconnect,
  1274. .flags = 0
  1275. },
  1276. {.name = "srv-clients-set",
  1277. .flags = VSH_CMD_FLAG_ALIAS,
  1278. .alias = "server-clients-set"
  1279. },
  1280. {.name = "server-clients-set",
  1281. .handler = cmdSrvClientsSet,
  1282. .opts = opts_srv_clients_set,
  1283. .info = info_srv_clients_set,
  1284. .flags = 0
  1285. },
  1286. {.name = "srv-update-tls",
  1287. .flags = VSH_CMD_FLAG_ALIAS,
  1288. .alias = "server-update-tls"
  1289. },
  1290. {.name = "server-update-tls",
  1291. .handler = cmdSrvUpdateTlsFiles,
  1292. .opts = opts_srv_update_tls_file,
  1293. .info = info_srv_update_tls_file,
  1294. .flags = 0
  1295. },
  1296. {.name = "daemon-log-filters",
  1297. .handler = cmdDaemonLogFilters,
  1298. .opts = opts_daemon_log_filters,
  1299. .info = info_daemon_log_filters,
  1300. .flags = 0
  1301. },
  1302. {.name = "daemon-log-outputs",
  1303. .handler = cmdDaemonLogOutputs,
  1304. .opts = opts_daemon_log_outputs,
  1305. .info = info_daemon_log_outputs,
  1306. .flags = 0
  1307. },
  1308. {.name = NULL}
  1309. };
  1310. static const vshCmdGrp cmdGroups[] = {
  1311. {"Virt-admin itself", "virt-admin", vshAdmCmds},
  1312. {"Monitoring commands", "monitor", monitoringCmds},
  1313. {"Management commands", "management", managementCmds},
  1314. {NULL, NULL, NULL}
  1315. };
  1316. static const vshClientHooks hooks = {
  1317. .connHandler = vshAdmConnectionHandler
  1318. };
  1319. int
  1320. main(int argc, char **argv)
  1321. {
  1322. vshControl _ctl, *ctl = &_ctl;
  1323. vshAdmControl virtAdminCtl;
  1324. bool ret = true;
  1325. memset(ctl, 0, sizeof(vshControl));
  1326. memset(&virtAdminCtl, 0, sizeof(vshAdmControl));
  1327. ctl->name = "virt-admin"; /* hardcoded name of the binary */
  1328. ctl->env_prefix = "VIRT_ADMIN";
  1329. ctl->log_fd = -1; /* Initialize log file descriptor */
  1330. ctl->debug = VSH_DEBUG_DEFAULT;
  1331. ctl->hooks = &hooks;
  1332. ctl->eventPipe[0] = -1;
  1333. ctl->eventPipe[1] = -1;
  1334. ctl->privData = &virtAdminCtl;
  1335. if (!(progname = strrchr(argv[0], '/')))
  1336. progname = argv[0];
  1337. else
  1338. progname++;
  1339. ctl->progname = progname;
  1340. if (virGettextInitialize() < 0)
  1341. return EXIT_FAILURE;
  1342. if (isatty(STDIN_FILENO)) {
  1343. ctl->istty = true;
  1344. #ifndef WIN32
  1345. if (tcgetattr(STDIN_FILENO, &ctl->termattr) < 0)
  1346. ctl->istty = false;
  1347. #endif
  1348. }
  1349. if (virMutexInit(&ctl->lock) < 0) {
  1350. vshError(ctl, "%s", _("Failed to initialize mutex"));
  1351. return EXIT_FAILURE;
  1352. }
  1353. if (virAdmInitialize() < 0) {
  1354. vshError(ctl, "%s", _("Failed to initialize libvirt"));
  1355. return EXIT_FAILURE;
  1356. }
  1357. virFileActivateDirOverrideForProg(argv[0]);
  1358. if (!vshInit(ctl, cmdGroups, NULL))
  1359. exit(EXIT_FAILURE);
  1360. if (!vshAdmParseArgv(ctl, argc, argv) ||
  1361. !vshAdmInit(ctl)) {
  1362. vshAdmDeinit(ctl);
  1363. exit(EXIT_FAILURE);
  1364. }
  1365. if (!ctl->imode) {
  1366. ret = vshCommandRun(ctl, ctl->cmd);
  1367. } else {
  1368. /* interactive mode */
  1369. if (!ctl->quiet) {
  1370. vshPrint(ctl,
  1371. _("Welcome to %s, the administrating virtualization "
  1372. "interactive terminal.\n\n"),
  1373. progname);
  1374. vshPrint(ctl, "%s",
  1375. _("Type: 'help' for help with commands\n"
  1376. " 'quit' to quit\n\n"));
  1377. }
  1378. do {
  1379. ctl->cmdstr = vshReadline(ctl, VIRT_ADMIN_PROMPT);
  1380. if (ctl->cmdstr == NULL)
  1381. break; /* EOF */
  1382. if (*ctl->cmdstr) {
  1383. #if WITH_READLINE
  1384. add_history(ctl->cmdstr);
  1385. #endif
  1386. if (vshCommandStringParse(ctl, ctl->cmdstr, NULL))
  1387. vshCommandRun(ctl, ctl->cmd);
  1388. }
  1389. VIR_FREE(ctl->cmdstr);
  1390. } while (ctl->imode);
  1391. if (ctl->cmdstr == NULL)
  1392. fputc('\n', stdout); /* line break after alone prompt */
  1393. }
  1394. vshAdmDeinit(ctl);
  1395. exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
  1396. }