1%% This Source Code Form is subject to the terms of the Mozilla Public 2%% License, v. 2.0. If a copy of the MPL was not distributed with this 3%% file, You can obtain one at https://mozilla.org/MPL/2.0/. 4%% 5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. 6%% 7 8-module(rabbit_vhost). 9 10-include_lib("rabbit_common/include/rabbit.hrl"). 11-include("vhost.hrl"). 12 13-export([recover/0, recover/1, read_config/1]). 14-export([add/2, add/4, delete/2, exists/1, with/2, with_user_and_vhost/3, assert/1, update/2, 15 set_limits/2, vhost_cluster_state/1, is_running_on_all_nodes/1, await_running_on_all_nodes/2, 16 list/0, count/0, list_names/0, all/0, all_tagged_with/1]). 17-export([parse_tags/1, update_metadata/2, tag_with/2, untag_from/2, update_tags/2, update_tags/3]). 18-export([lookup/1]). 19-export([info/1, info/2, info_all/0, info_all/1, info_all/2, info_all/3]). 20-export([dir/1, msg_store_dir_path/1, msg_store_dir_wildcard/0, config_file_path/1, ensure_config_file/1]). 21-export([delete_storage/1]). 22-export([vhost_down/1]). 23-export([put_vhost/5]). 24 25%% 26%% API 27%% 28 29-type vhost_tag() :: atom() | string() | binary(). 30-export_type([vhost_tag/0]). 31 32recover() -> 33 %% Clear out remnants of old incarnation, in case we restarted 34 %% faster than other nodes handled DOWN messages from us. 35 rabbit_amqqueue:on_node_down(node()), 36 37 rabbit_amqqueue:warn_file_limit(), 38 39 %% Prepare rabbit_semi_durable_route table 40 {Time, _} = timer:tc(fun() -> 41 rabbit_binding:recover() 42 end), 43 rabbit_log:debug("rabbit_binding:recover/0 completed in ~fs", [Time/1000000]), 44 45 %% rabbit_vhost_sup_sup will start the actual recovery. 46 %% So recovery will be run every time a vhost supervisor is restarted. 47 ok = rabbit_vhost_sup_sup:start(), 48 49 [ok = rabbit_vhost_sup_sup:init_vhost(VHost) || VHost <- list_names()], 50 ok. 51 52recover(VHost) -> 53 VHostDir = msg_store_dir_path(VHost), 54 rabbit_log:info("Making sure data directory '~ts' for vhost '~s' exists", 55 [VHostDir, VHost]), 56 VHostStubFile = filename:join(VHostDir, ".vhost"), 57 ok = rabbit_file:ensure_dir(VHostStubFile), 58 ok = file:write_file(VHostStubFile, VHost), 59 ok = ensure_config_file(VHost), 60 {Recovered, Failed} = rabbit_amqqueue:recover(VHost), 61 AllQs = Recovered ++ Failed, 62 QNames = [amqqueue:get_name(Q) || Q <- AllQs], 63 {Time, ok} = timer:tc(fun() -> 64 rabbit_binding:recover(rabbit_exchange:recover(VHost), QNames) 65 end), 66 rabbit_log:debug("rabbit_binding:recover/2 for vhost ~s completed in ~fs", [VHost, Time/1000000]), 67 68 ok = rabbit_amqqueue:start(Recovered), 69 %% Start queue mirrors. 70 ok = rabbit_mirror_queue_misc:on_vhost_up(VHost), 71 ok. 72 73ensure_config_file(VHost) -> 74 Path = config_file_path(VHost), 75 case filelib:is_regular(Path) of 76 %% The config file exists. Do nothing. 77 true -> 78 ok; 79 %% The config file does not exist. 80 %% Check if there are queues in this vhost. 81 false -> 82 QueueDirs = rabbit_queue_index:all_queue_directory_names(VHost), 83 SegmentEntryCount = case QueueDirs of 84 %% There are no queues. Write the configured value for 85 %% the segment entry count, or the new RabbitMQ default 86 %% introduced in v3.8.17. The new default provides much 87 %% better memory footprint when many queues are used. 88 [] -> 89 application:get_env(rabbit, queue_index_segment_entry_count, 90 2048); 91 %% There are queues already. Write the historic RabbitMQ 92 %% default of 16384 for forward compatibility. Historic 93 %% default calculated as trunc(math:pow(2,?REL_SEQ_BITS)). 94 _ -> 95 ?LEGACY_INDEX_SEGMENT_ENTRY_COUNT 96 end, 97 rabbit_log:info("Setting segment_entry_count for vhost '~s' with ~b queues to '~b'", 98 [VHost, length(QueueDirs), SegmentEntryCount]), 99 file:write_file(Path, io_lib:format( 100 "%% This file is auto-generated! Edit at your own risk!~n" 101 "{segment_entry_count, ~b}.", 102 [SegmentEntryCount])) 103 end. 104 105read_config(VHost) -> 106 Config = case file:consult(config_file_path(VHost)) of 107 {ok, Val} -> Val; 108 %% the file does not exist yet, likely due to an upgrade from a pre-3.7 109 %% message store layout so use the history default. 110 {error, _} -> #{ 111 segment_entry_count => ?LEGACY_INDEX_SEGMENT_ENTRY_COUNT 112 } 113 end, 114 rabbit_data_coercion:to_map(Config). 115 116-define(INFO_KEYS, vhost:info_keys()). 117 118-spec parse_tags(binary() | string() | atom()) -> [atom()]. 119parse_tags(undefined) -> 120 []; 121parse_tags(<<"">>) -> 122 []; 123parse_tags([]) -> 124 []; 125parse_tags(Val) when is_binary(Val) -> 126 SVal = rabbit_data_coercion:to_list(Val), 127 [trim_tag(Tag) || Tag <- re:split(SVal, ",", [{return, list}])]; 128parse_tags(Val) when is_list(Val) -> 129 case hd(Val) of 130 Bin when is_binary(Bin) -> 131 %% this is a list of binaries 132 [trim_tag(Tag) || Tag <- Val]; 133 Atom when is_atom(Atom) -> 134 %% this is a list of atoms 135 [trim_tag(Tag) || Tag <- Val]; 136 Int when is_integer(Int) -> 137 %% this is a string/charlist 138 [trim_tag(Tag) || Tag <- re:split(Val, ",", [{return, list}])] 139 end. 140 141-spec add(vhost:name(), rabbit_types:username()) -> rabbit_types:ok_or_error(any()). 142 143add(VHost, ActingUser) -> 144 case exists(VHost) of 145 true -> ok; 146 false -> do_add(VHost, <<"">>, [], ActingUser) 147 end. 148 149-spec add(vhost:name(), binary(), [atom()], rabbit_types:username()) -> rabbit_types:ok_or_error(any()). 150 151add(Name, Description, Tags, ActingUser) -> 152 case exists(Name) of 153 true -> ok; 154 false -> do_add(Name, Description, Tags, ActingUser) 155 end. 156 157do_add(Name, Description, Tags, ActingUser) -> 158 case Description of 159 undefined -> 160 rabbit_log:info("Adding vhost '~s' without a description", [Name]); 161 Value -> 162 rabbit_log:info("Adding vhost '~s' (description: '~s', tags: ~p)", [Name, Value, Tags]) 163 end, 164 VHost = rabbit_misc:execute_mnesia_transaction( 165 fun () -> 166 case mnesia:wread({rabbit_vhost, Name}) of 167 [] -> 168 Row = vhost:new(Name, [], #{description => Description, tags => Tags}), 169 rabbit_log:debug("Inserting a virtual host record ~p", [Row]), 170 ok = mnesia:write(rabbit_vhost, Row, write), 171 Row; 172 %% the vhost already exists 173 [Row] -> 174 Row 175 end 176 end, 177 fun (VHost1, true) -> 178 VHost1; 179 (VHost1, false) -> 180 [begin 181 Resource = rabbit_misc:r(Name, exchange, ExchangeName), 182 rabbit_log:debug("Will declare an exchange ~p", [Resource]), 183 _ = rabbit_exchange:declare(Resource, Type, true, false, Internal, [], ActingUser) 184 end || {ExchangeName, Type, Internal} <- 185 [{<<"">>, direct, false}, 186 {<<"amq.direct">>, direct, false}, 187 {<<"amq.topic">>, topic, false}, 188 %% per 0-9-1 pdf 189 {<<"amq.match">>, headers, false}, 190 %% per 0-9-1 xml 191 {<<"amq.headers">>, headers, false}, 192 {<<"amq.fanout">>, fanout, false}, 193 {<<"amq.rabbitmq.trace">>, topic, true}]], 194 VHost1 195 end), 196 case rabbit_vhost_sup_sup:start_on_all_nodes(Name) of 197 ok -> 198 rabbit_event:notify(vhost_created, info(VHost) 199 ++ [{user_who_performed_action, ActingUser}, 200 {description, Description}, 201 {tags, Tags}]), 202 ok; 203 {error, Reason} -> 204 Msg = rabbit_misc:format("failed to set up vhost '~s': ~p", 205 [Name, Reason]), 206 {error, Msg} 207 end. 208 209-spec update(vhost:name(), binary(), [atom()], rabbit_types:username()) -> rabbit_types:ok_or_error(any()). 210update(Name, Description, Tags, ActingUser) -> 211 rabbit_misc:execute_mnesia_transaction( 212 fun () -> 213 case mnesia:wread({rabbit_vhost, Name}) of 214 [] -> 215 {error, {no_such_vhost, Name}}; 216 [VHost0] -> 217 VHost = vhost:merge_metadata(VHost0, #{description => Description, tags => Tags}), 218 rabbit_log:debug("Updating a virtual host record ~p", [VHost]), 219 ok = mnesia:write(rabbit_vhost, VHost, write), 220 rabbit_event:notify(vhost_updated, info(VHost) 221 ++ [{user_who_performed_action, ActingUser}, 222 {description, Description}, 223 {tags, Tags}]), 224 ok 225 end 226 end). 227 228 229-spec delete(vhost:name(), rabbit_types:username()) -> rabbit_types:ok_or_error(any()). 230 231delete(VHost, ActingUser) -> 232 %% FIXME: We are forced to delete the queues and exchanges outside 233 %% the TX below. Queue deletion involves sending messages to the queue 234 %% process, which in turn results in further mnesia actions and 235 %% eventually the termination of that process. Exchange deletion causes 236 %% notifications which must be sent outside the TX 237 rabbit_log:info("Deleting vhost '~s'", [VHost]), 238 QDelFun = fun (Q) -> rabbit_amqqueue:delete(Q, false, false, ActingUser) end, 239 [begin 240 Name = amqqueue:get_name(Q), 241 assert_benign(rabbit_amqqueue:with(Name, QDelFun), ActingUser) 242 end || Q <- rabbit_amqqueue:list(VHost)], 243 [assert_benign(rabbit_exchange:delete(Name, false, ActingUser), ActingUser) || 244 #exchange{name = Name} <- rabbit_exchange:list(VHost)], 245 Funs = rabbit_misc:execute_mnesia_transaction( 246 with(VHost, fun () -> internal_delete(VHost, ActingUser) end)), 247 ok = rabbit_event:notify(vhost_deleted, [{name, VHost}, 248 {user_who_performed_action, ActingUser}]), 249 [case Fun() of 250 ok -> ok; 251 {error, {no_such_vhost, VHost}} -> ok 252 end || Fun <- Funs], 253 %% After vhost was deleted from mnesia DB, we try to stop vhost supervisors 254 %% on all the nodes. 255 rabbit_vhost_sup_sup:delete_on_all_nodes(VHost), 256 ok. 257 258put_vhost(Name, Description, Tags0, Trace, Username) -> 259 Tags = case Tags0 of 260 undefined -> <<"">>; 261 null -> <<"">>; 262 "undefined" -> <<"">>; 263 "null" -> <<"">>; 264 Other -> Other 265 end, 266 ParsedTags = parse_tags(Tags), 267 rabbit_log:debug("Parsed tags ~p to ~p", [Tags, ParsedTags]), 268 Result = case exists(Name) of 269 true -> 270 update(Name, Description, ParsedTags, Username); 271 false -> 272 add(Name, Description, ParsedTags, Username), 273 %% wait for up to 45 seconds for the vhost to initialise 274 %% on all nodes 275 case await_running_on_all_nodes(Name, 45000) of 276 ok -> 277 maybe_grant_full_permissions(Name, Username); 278 {error, timeout} -> 279 {error, timeout} 280 end 281 end, 282 case Trace of 283 true -> rabbit_trace:start(Name); 284 false -> rabbit_trace:stop(Name); 285 undefined -> ok 286 end, 287 Result. 288 289%% when definitions are loaded on boot, Username here will be ?INTERNAL_USER, 290%% which does not actually exist 291maybe_grant_full_permissions(_Name, ?INTERNAL_USER) -> 292 ok; 293maybe_grant_full_permissions(Name, Username) -> 294 U = rabbit_auth_backend_internal:lookup_user(Username), 295 maybe_grant_full_permissions(U, Name, Username). 296 297maybe_grant_full_permissions({ok, _}, Name, Username) -> 298 rabbit_auth_backend_internal:set_permissions( 299 Username, Name, <<".*">>, <<".*">>, <<".*">>, Username); 300maybe_grant_full_permissions(_, _Name, _Username) -> 301 ok. 302 303 304%% 50 ms 305-define(AWAIT_SAMPLE_INTERVAL, 50). 306 307-spec await_running_on_all_nodes(vhost:name(), integer()) -> ok | {error, timeout}. 308await_running_on_all_nodes(VHost, Timeout) -> 309 Attempts = round(Timeout / ?AWAIT_SAMPLE_INTERVAL), 310 await_running_on_all_nodes0(VHost, Attempts). 311 312await_running_on_all_nodes0(_VHost, 0) -> 313 {error, timeout}; 314await_running_on_all_nodes0(VHost, Attempts) -> 315 case is_running_on_all_nodes(VHost) of 316 true -> ok; 317 _ -> 318 timer:sleep(?AWAIT_SAMPLE_INTERVAL), 319 await_running_on_all_nodes0(VHost, Attempts - 1) 320 end. 321 322-spec is_running_on_all_nodes(vhost:name()) -> boolean(). 323is_running_on_all_nodes(VHost) -> 324 States = vhost_cluster_state(VHost), 325 lists:all(fun ({_Node, State}) -> State =:= running end, 326 States). 327 328-spec vhost_cluster_state(vhost:name()) -> [{atom(), atom()}]. 329vhost_cluster_state(VHost) -> 330 Nodes = rabbit_nodes:all_running(), 331 lists:map(fun(Node) -> 332 State = case rabbit_misc:rpc_call(Node, 333 rabbit_vhost_sup_sup, is_vhost_alive, 334 [VHost]) of 335 {badrpc, nodedown} -> nodedown; 336 true -> running; 337 false -> stopped 338 end, 339 {Node, State} 340 end, 341 Nodes). 342 343vhost_down(VHost) -> 344 ok = rabbit_event:notify(vhost_down, 345 [{name, VHost}, 346 {node, node()}, 347 {user_who_performed_action, ?INTERNAL_USER}]). 348 349delete_storage(VHost) -> 350 VhostDir = msg_store_dir_path(VHost), 351 rabbit_log:info("Deleting message store directory for vhost '~s' at '~s'", [VHost, VhostDir]), 352 %% Message store should be closed when vhost supervisor is closed. 353 case rabbit_file:recursive_delete([VhostDir]) of 354 ok -> ok; 355 {error, {_, enoent}} -> 356 %% a concurrent delete did the job for us 357 rabbit_log:warning("Tried to delete storage directories for vhost '~s', it failed with an ENOENT", [VHost]), 358 ok; 359 Other -> 360 rabbit_log:warning("Tried to delete storage directories for vhost '~s': ~p", [VHost, Other]), 361 Other 362 end. 363 364assert_benign(ok, _) -> ok; 365assert_benign({ok, _}, _) -> ok; 366assert_benign({ok, _, _}, _) -> ok; 367assert_benign({error, not_found}, _) -> ok; 368assert_benign({error, {absent, Q, _}}, ActingUser) -> 369 %% Removing the mnesia entries here is safe. If/when the down node 370 %% restarts, it will clear out the on-disk storage of the queue. 371 QName = amqqueue:get_name(Q), 372 rabbit_amqqueue:internal_delete(QName, ActingUser). 373 374internal_delete(VHost, ActingUser) -> 375 [ok = rabbit_auth_backend_internal:clear_permissions( 376 proplists:get_value(user, Info), VHost, ActingUser) 377 || Info <- rabbit_auth_backend_internal:list_vhost_permissions(VHost)], 378 TopicPermissions = rabbit_auth_backend_internal:list_vhost_topic_permissions(VHost), 379 [ok = rabbit_auth_backend_internal:clear_topic_permissions( 380 proplists:get_value(user, TopicPermission), VHost, ActingUser) 381 || TopicPermission <- TopicPermissions], 382 Fs1 = [rabbit_runtime_parameters:clear(VHost, 383 proplists:get_value(component, Info), 384 proplists:get_value(name, Info), 385 ActingUser) 386 || Info <- rabbit_runtime_parameters:list(VHost)], 387 Fs2 = [rabbit_policy:delete(VHost, proplists:get_value(name, Info), ActingUser) 388 || Info <- rabbit_policy:list(VHost)], 389 ok = mnesia:delete({rabbit_vhost, VHost}), 390 Fs1 ++ Fs2. 391 392-spec exists(vhost:name()) -> boolean(). 393 394exists(VHost) -> 395 mnesia:dirty_read({rabbit_vhost, VHost}) /= []. 396 397-spec list_names() -> [vhost:name()]. 398list_names() -> mnesia:dirty_all_keys(rabbit_vhost). 399 400%% Exists for backwards compatibility, prefer list_names/0. 401-spec list() -> [vhost:name()]. 402list() -> list_names(). 403 404-spec all() -> [vhost:vhost()]. 405all() -> mnesia:dirty_match_object(rabbit_vhost, vhost:pattern_match_all()). 406 407-spec all_tagged_with(atom()) -> [vhost:vhost()]. 408all_tagged_with(TagName) -> 409 lists:filter( 410 fun(VHost) -> 411 Meta = vhost:get_metadata(VHost), 412 case Meta of 413 #{tags := Tags} -> 414 lists:member(rabbit_data_coercion:to_atom(TagName), Tags); 415 _ -> false 416 end 417 end, all()). 418 419-spec count() -> non_neg_integer(). 420count() -> 421 length(list()). 422 423-spec lookup(vhost:name()) -> vhost:vhost() | rabbit_types:ok_or_error(any()). 424lookup(VHostName) -> 425 case rabbit_misc:dirty_read({rabbit_vhost, VHostName}) of 426 {error, not_found} -> {error, {no_such_vhost, VHostName}}; 427 {ok, Record} -> Record 428 end. 429 430-spec with(vhost:name(), rabbit_misc:thunk(A)) -> A. 431with(VHostName, Thunk) -> 432 fun () -> 433 case mnesia:read({rabbit_vhost, VHostName}) of 434 [] -> mnesia:abort({no_such_vhost, VHostName}); 435 [_V] -> Thunk() 436 end 437 end. 438 439-spec with_user_and_vhost(rabbit_types:username(), vhost:name(), rabbit_misc:thunk(A)) -> A. 440with_user_and_vhost(Username, VHostName, Thunk) -> 441 rabbit_misc:with_user(Username, with(VHostName, Thunk)). 442 443%% Like with/2 but outside an Mnesia tx 444 445-spec assert(vhost:name()) -> 'ok'. 446assert(VHostName) -> 447 case exists(VHostName) of 448 true -> ok; 449 false -> throw({error, {no_such_vhost, VHostName}}) 450 end. 451 452-spec update(vhost:name(), fun((vhost:vhost()) -> vhost:vhost())) -> vhost:vhost(). 453update(VHostName, Fun) -> 454 case mnesia:read({rabbit_vhost, VHostName}) of 455 [] -> 456 mnesia:abort({no_such_vhost, VHostName}); 457 [V] -> 458 V1 = Fun(V), 459 ok = mnesia:write(rabbit_vhost, V1, write), 460 V1 461 end. 462 463-spec update_metadata(vhost:name(), fun((map())-> map())) -> vhost:vhost() | rabbit_types:ok_or_error(any()). 464update_metadata(VHostName, Fun) -> 465 update(VHostName, fun(Record) -> 466 Meta = Fun(vhost:get_metadata(Record)), 467 vhost:set_metadata(Record, Meta) 468 end). 469 470-spec update_tags(vhost:name(), [vhost_tag()], rabbit_types:username()) -> vhost:vhost() | rabbit_types:ok_or_error(any()). 471update_tags(VHostName, Tags, ActingUser) -> 472 ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags], 473 try 474 R = rabbit_misc:execute_mnesia_transaction(fun() -> 475 update_tags(VHostName, ConvertedTags) 476 end), 477 rabbit_log:info("Successfully set tags for virtual host '~s' to ~p", [VHostName, ConvertedTags]), 478 rabbit_event:notify(vhost_tags_set, [{name, VHostName}, 479 {tags, ConvertedTags}, 480 {user_who_performed_action, ActingUser}]), 481 R 482 catch 483 throw:{error, {no_such_vhost, _}} = Error -> 484 rabbit_log:warning("Failed to set tags for virtual host '~s': the virtual host does not exist", [VHostName]), 485 throw(Error); 486 throw:Error -> 487 rabbit_log:warning("Failed to set tags for virtual host '~s': ~p", [VHostName, Error]), 488 throw(Error); 489 exit:Error -> 490 rabbit_log:warning("Failed to set tags for virtual host '~s': ~p", [VHostName, Error]), 491 exit(Error) 492 end. 493 494-spec update_tags(vhost:name(), [vhost_tag()]) -> vhost:vhost() | rabbit_types:ok_or_error(any()). 495update_tags(VHostName, Tags) -> 496 ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags], 497 update(VHostName, fun(Record) -> 498 Meta0 = vhost:get_metadata(Record), 499 Meta = maps:update(tags, ConvertedTags, Meta0), 500 vhost:set_metadata(Record, Meta) 501 end). 502 503-spec tag_with(vhost:name(), [atom()]) -> vhost:vhost() | rabbit_types:ok_or_error(any()). 504tag_with(VHostName, Tags) when is_list(Tags) -> 505 update_metadata(VHostName, fun(#{tags := Tags0} = Meta) -> 506 maps:update(tags, lists:usort(Tags0 ++ Tags), Meta) 507 end). 508 509-spec untag_from(vhost:name(), [atom()]) -> vhost:vhost() | rabbit_types:ok_or_error(any()). 510untag_from(VHostName, Tags) when is_list(Tags) -> 511 update_metadata(VHostName, fun(#{tags := Tags0} = Meta) -> 512 maps:update(tags, lists:usort(Tags0 -- Tags), Meta) 513 end). 514 515set_limits(VHost, undefined) -> 516 vhost:set_limits(VHost, []); 517set_limits(VHost, Limits) -> 518 vhost:set_limits(VHost, Limits). 519 520 521dir(Vhost) -> 522 <<Num:128>> = erlang:md5(Vhost), 523 rabbit_misc:format("~.36B", [Num]). 524 525msg_store_dir_path(VHost) -> 526 EncodedName = dir(VHost), 527 rabbit_data_coercion:to_list(filename:join([msg_store_dir_base(), EncodedName])). 528 529msg_store_dir_wildcard() -> 530 rabbit_data_coercion:to_list(filename:join([msg_store_dir_base(), "*"])). 531 532msg_store_dir_base() -> 533 Dir = rabbit_mnesia:dir(), 534 filename:join([Dir, "msg_stores", "vhosts"]). 535 536config_file_path(VHost) -> 537 VHostDir = msg_store_dir_path(VHost), 538 filename:join(VHostDir, ".config"). 539 540-spec trim_tag(list() | binary() | atom()) -> atom(). 541trim_tag(Val) -> 542 rabbit_data_coercion:to_atom(string:trim(rabbit_data_coercion:to_list(Val))). 543 544%%---------------------------------------------------------------------------- 545 546infos(Items, X) -> [{Item, i(Item, X)} || Item <- Items]. 547 548i(name, VHost) -> vhost:get_name(VHost); 549i(tracing, VHost) -> rabbit_trace:enabled(vhost:get_name(VHost)); 550i(cluster_state, VHost) -> vhost_cluster_state(vhost:get_name(VHost)); 551i(description, VHost) -> vhost:get_description(VHost); 552i(tags, VHost) -> vhost:get_tags(VHost); 553i(metadata, VHost) -> vhost:get_metadata(VHost); 554i(Item, VHost) -> 555 rabbit_log:error("Don't know how to compute a virtual host info item '~s' for virtual host '~p'", [Item, VHost]), 556 throw({bad_argument, Item}). 557 558-spec info(vhost:vhost() | vhost:name()) -> rabbit_types:infos(). 559 560info(VHost) when ?is_vhost(VHost) -> 561 infos(?INFO_KEYS, VHost); 562info(Key) -> 563 case mnesia:dirty_read({rabbit_vhost, Key}) of 564 [] -> []; 565 [VHost] -> infos(?INFO_KEYS, VHost) 566 end. 567 568-spec info(vhost:vhost(), rabbit_types:info_keys()) -> rabbit_types:infos(). 569info(VHost, Items) -> infos(Items, VHost). 570 571-spec info_all() -> [rabbit_types:infos()]. 572info_all() -> info_all(?INFO_KEYS). 573 574-spec info_all(rabbit_types:info_keys()) -> [rabbit_types:infos()]. 575info_all(Items) -> [info(VHost, Items) || VHost <- all()]. 576 577info_all(Ref, AggregatorPid) -> info_all(?INFO_KEYS, Ref, AggregatorPid). 578 579-spec info_all(rabbit_types:info_keys(), reference(), pid()) -> 'ok'. 580info_all(Items, Ref, AggregatorPid) -> 581 rabbit_control_misc:emitting_map( 582 AggregatorPid, Ref, fun(VHost) -> info(VHost, Items) end, all()). 583