gproc_dist_tests.erl 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with this software. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% @author Ulf Wiger <ulf.wiger@erlang-solutions.com>
  17. %%
  18. -module(gproc_dist_tests).
  19. -ifdef(TEST).
  20. -include_lib("eunit/include/eunit.hrl").
  21. -export([t_spawn/1, t_spawn_reg/2]).
  22. dist_test_() ->
  23. {timeout, 120,
  24. [{setup,
  25. fun() ->
  26. case run_dist_tests() of
  27. true ->
  28. Ns = start_slaves([dist_test_n1, dist_test_n2]),
  29. ?assertMatch({[ok,ok],[]},
  30. rpc:multicall(Ns, application, set_env,
  31. [gproc, gproc_dist, Ns])),
  32. ?assertMatch({[ok,ok],[]},
  33. rpc:multicall(
  34. Ns, application, start, [gproc])),
  35. Ns;
  36. false ->
  37. skip
  38. end
  39. end,
  40. fun(_Ns) ->
  41. ok
  42. end,
  43. fun(skip) -> [];
  44. (Ns) when is_list(Ns) ->
  45. {inorder,
  46. [
  47. {inparallel, [
  48. fun() ->
  49. ?debugVal(t_simple_reg(Ns))
  50. end,
  51. fun() ->
  52. ?debugVal(t_simple_reg_or_locate(Ns))
  53. end,
  54. fun() ->
  55. ?debugVal(t_simple_counter(Ns))
  56. end,
  57. fun() ->
  58. ?debugVal(t_aggr_counter(Ns))
  59. end,
  60. fun() ->
  61. ?debugVal(t_update_counters(Ns))
  62. end,
  63. fun() ->
  64. ?debugVal(t_shared_counter(Ns))
  65. end,
  66. fun() ->
  67. ?debugVal(t_mreg(Ns))
  68. end,
  69. fun() ->
  70. ?debugVal(t_await_reg(Ns))
  71. end,
  72. fun() ->
  73. ?debugVal(t_await_self(Ns))
  74. end,
  75. fun() ->
  76. ?debugVal(t_await_reg_exists(Ns))
  77. end,
  78. fun() ->
  79. ?debugVal(t_give_away(Ns))
  80. end,
  81. fun() ->
  82. ?debugVal(t_sync(Ns))
  83. end,
  84. fun() ->
  85. ?debugVal(t_monitor(Ns))
  86. end,
  87. fun() ->
  88. ?debugVal(t_subscribe(Ns))
  89. end
  90. ]
  91. },
  92. fun() ->
  93. ?debugVal(t_sync_cand_dies(Ns))
  94. end,
  95. {timeout, 90, [fun() ->
  96. ?debugVal(t_fail_node(Ns))
  97. end]}
  98. ]}
  99. end
  100. }]}.
  101. run_dist_tests() ->
  102. case os:getenv("GPROC_DIST") of
  103. "true" -> true;
  104. "false" -> false;
  105. false ->
  106. case code:ensure_loaded(gen_leader) of
  107. {error, nofile} ->
  108. false;
  109. _ ->
  110. true
  111. end
  112. end.
  113. -define(T_NAME, {n, g, {?MODULE, ?LINE}}).
  114. -define(T_KVL, [{foo, "foo"}, {bar, "bar"}]).
  115. -define(T_COUNTER, {c, g, {?MODULE, ?LINE}}).
  116. t_simple_reg([H|_] = Ns) ->
  117. Name = ?T_NAME,
  118. P = t_spawn_reg(H, Name),
  119. ?assertMatch(ok, t_lookup_everywhere(Name, Ns, P)),
  120. ?assertMatch(true, t_call(P, {apply, gproc, unreg, [Name]})),
  121. ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
  122. ?assertMatch(ok, t_call(P, die)).
  123. t_simple_reg_or_locate([A,B|_] = _Ns) ->
  124. Name = ?T_NAME,
  125. P1 = t_spawn(A),
  126. _Ref = erlang:monitor(process, P1),
  127. ?assertMatch({P1, the_value},
  128. t_call(P1, {apply, gproc, reg_or_locate, [Name, the_value]})),
  129. P2 = t_spawn(B),
  130. _Ref2 = erlang:monitor(process, P2),
  131. ?assertMatch({P1, the_value},
  132. t_call(P2, {apply, gproc, reg_or_locate, [Name, other_value]})),
  133. ?assertMatch(ok, t_call(P1, die)),
  134. ?assertMatch(ok, t_call(P2, die)).
  135. t_simple_counter([H|_] = Ns) ->
  136. Ctr = ?T_COUNTER,
  137. P = t_spawn_reg(H, Ctr, 3),
  138. ?assertMatch(ok, t_read_everywhere(Ctr, P, Ns, 3)),
  139. ?assertMatch(5, t_call(P, {apply, gproc, update_counter, [Ctr, 2]})),
  140. ?assertMatch(ok, t_read_everywhere(Ctr, P, Ns, 5)),
  141. ?assertMatch(ok, t_call(P, die)).
  142. t_shared_counter([H|_] = Ns) ->
  143. Ctr = ?T_COUNTER,
  144. P = t_spawn_reg_shared(H, Ctr, 3),
  145. ?assertMatch(ok, t_read_everywhere(Ctr, shared, Ns, 3)),
  146. ?assertMatch(5, t_call(P, {apply, gproc, update_shared_counter, [Ctr, 2]})),
  147. ?assertMatch(ok, t_read_everywhere(Ctr, shared, Ns, 5)),
  148. ?assertMatch(ok, t_call(P, die)),
  149. ?assertMatch(ok, t_read_everywhere(Ctr, shared, Ns, 5)),
  150. ?assertMatch(ok, t_read_everywhere(Ctr, shared, Ns, 5)), % twice
  151. P1 = t_spawn(H),
  152. ?assertMatch(true, t_call(P1, {apply, gproc, unreg_shared, [Ctr]})),
  153. ?assertMatch(ok, t_read_everywhere(Ctr, shared, Ns, badarg)).
  154. t_aggr_counter([H1,H2|_] = Ns) ->
  155. {c,g,Nm} = Ctr = ?T_COUNTER,
  156. Aggr = {a,g,Nm},
  157. Pc1 = t_spawn_reg(H1, Ctr, 3),
  158. Pa = t_spawn_reg(H2, Aggr),
  159. ?assertMatch(ok, t_read_everywhere(Ctr, Pc1, Ns, 3)),
  160. ?assertMatch(ok, t_read_everywhere(Aggr, Pa, Ns, 3)),
  161. Pc2 = t_spawn_reg(H2, Ctr, 3),
  162. ?assertMatch(ok, t_read_everywhere(Ctr, Pc2, Ns, 3)),
  163. ?assertMatch(ok, t_read_everywhere(Aggr, Pa, Ns, 6)),
  164. ?assertMatch(5, t_call(Pc1, {apply, gproc, update_counter, [Ctr, 2]})),
  165. ?assertMatch(ok, t_read_everywhere(Ctr, Pc1, Ns, 5)),
  166. ?assertMatch(ok, t_read_everywhere(Aggr, Pa, Ns, 8)),
  167. ?assertMatch(ok, t_call(Pc1, die)),
  168. ?assertMatch(ok, t_read_everywhere(Aggr, Pa, Ns, 3)),
  169. ?assertMatch(ok, t_call(Pc2, die)),
  170. ?assertMatch(ok, t_call(Pa, die)).
  171. t_update_counters([H1,H2|_] = Ns) ->
  172. {c,g,N1} = C1 = ?T_COUNTER,
  173. A1 = {a,g,N1},
  174. C2 = ?T_COUNTER,
  175. P1 = t_spawn_reg(H1, C1, 2),
  176. P12 = t_spawn_reg(H2, C1, 2),
  177. P2 = t_spawn_reg(H2, C2, 1),
  178. Pa1 = t_spawn_reg(H2, A1),
  179. ?assertMatch(ok, t_read_everywhere(C1, P1, Ns, 2)),
  180. ?assertMatch(ok, t_read_everywhere(C1, P12, Ns, 2)),
  181. ?assertMatch(ok, t_read_everywhere(C2, P2, Ns, 1)),
  182. ?assertMatch(ok, t_read_everywhere(A1, Pa1, Ns, 4)),
  183. ?debugFmt("code:which(gproc_dist) = ~p~n", [code:which(gproc_dist)]),
  184. ?assertMatch([{C1,P1, 3},
  185. {C1,P12,4},
  186. {C2,P2, 0}], t_call(P1, {apply, gproc, update_counters,
  187. [g, [{C1,P1,1},{C1,P12,2},{C2,P2,{-2,0,0}}]]})),
  188. ?assertMatch(ok, t_read_everywhere(C1, P1, Ns, 3)),
  189. ?assertMatch(ok, t_read_everywhere(C1, P12, Ns, 4)),
  190. ?assertMatch(ok, t_read_everywhere(C2, P2, Ns, 0)),
  191. ?assertMatch(ok, t_read_everywhere(A1, Pa1, Ns, 7)),
  192. ?assertMatch(ok, t_call(P1, die)),
  193. ?assertMatch(ok, t_call(P12, die)),
  194. ?assertMatch(ok, t_call(P2, die)).
  195. t_mreg([H|_] = Ns) ->
  196. Kvl = ?T_KVL,
  197. Keys = [K || {K,_} <- Kvl],
  198. P = t_spawn_mreg(H, Kvl),
  199. [?assertMatch(ok, t_lookup_everywhere({n,g,K}, Ns, P)) || K <- Keys],
  200. ?assertMatch(true, t_call(P, {apply, gproc, munreg, [n, g, Keys]})),
  201. [?assertMatch(ok, t_lookup_everywhere({n,g,K},Ns,undefined)) || K <- Keys],
  202. ?assertMatch(ok, t_call(P, die)).
  203. t_await_reg([A,B|_]) ->
  204. Name = ?T_NAME,
  205. P = t_spawn(A),
  206. Ref = erlang:monitor(process, P),
  207. P ! {self(), Ref, {apply, gproc, await, [Name]}},
  208. t_sleep(),
  209. P1 = t_spawn_reg(B, Name),
  210. ?assert(P1 == receive
  211. {P, Ref, Res} ->
  212. element(1, Res);
  213. {'DOWN', Ref, _, _, Reason} ->
  214. erlang:error(Reason);
  215. Other ->
  216. erlang:error({received,Other})
  217. end),
  218. ?assertMatch(ok, t_call(P, die)),
  219. ?assertMatch(ok, t_call(P1, die)).
  220. t_await_self([A|_]) ->
  221. Name = ?T_NAME,
  222. P = t_spawn(A, false), % don't buffer unknowns
  223. Ref = t_call(P, {apply, gproc, nb_wait, [Name]}),
  224. ?assertMatch(ok, t_call(P, {selective, true})),
  225. ?assertMatch(true, t_call(P, {apply, gproc, reg, [Name, some_value]})),
  226. ?assertMatch({registered, {Name, P, some_value}},
  227. t_call(P, {apply_fun, fun() ->
  228. receive
  229. {gproc, Ref, R, Wh} ->
  230. {R, Wh}
  231. after 10000 ->
  232. timeout
  233. end
  234. end})),
  235. ?assertMatch(ok, t_call(P, {selective, false})),
  236. ?assertMatch(true, t_call(P, {apply, gproc, unreg, [Name]})).
  237. t_await_reg_exists([A,B|_]) ->
  238. Name = ?T_NAME,
  239. P = t_spawn(A),
  240. Ref = erlang:monitor(process, P),
  241. P1 = t_spawn_reg(B, Name),
  242. P ! {self(), Ref, {apply, gproc, await, [Name]}},
  243. ?assert(P1 == receive
  244. {P, Ref, Res} ->
  245. element(1, Res);
  246. {'DOWN', Ref, _, _, Reason} ->
  247. erlang:error(Reason);
  248. Other ->
  249. erlang:error({received,Other})
  250. end),
  251. ?assertMatch(ok, t_call(P, die)),
  252. ?assertMatch(ok, t_call(P1, die)).
  253. t_give_away([A,B|_] = Ns) ->
  254. Na = ?T_NAME,
  255. Nb = ?T_NAME,
  256. Pa = t_spawn_reg(A, Na),
  257. Pb = t_spawn_reg(B, Nb),
  258. ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
  259. ?assertMatch(ok, t_lookup_everywhere(Nb, Ns, Pb)),
  260. ?assertMatch(Pb, t_call(Pa, {apply, gproc, give_away, [Na, Nb]})),
  261. ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pb)),
  262. ?assertMatch(Pa, t_call(Pb, {apply, gproc, give_away, [Na, Pa]})),
  263. ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
  264. ?assertMatch(ok, t_call(Pa, die)),
  265. ?assertMatch(ok, t_call(Pb, die)).
  266. t_sync(Ns) ->
  267. %% Don't really know how to test this...
  268. [?assertMatch(true, rpc:call(N, gproc_dist, sync, []))
  269. || N <- Ns].
  270. t_monitor([A,B|_]) ->
  271. Na = ?T_NAME,
  272. Pa = t_spawn_reg(A, Na),
  273. Pb = t_spawn(B, _Selective = true),
  274. Ref = t_call(Pb, {apply, gproc, monitor, [Na]}),
  275. ?assert(is_reference(Ref)),
  276. ?assertMatch(ok, t_call(Pa, die)),
  277. ?assertMatch({gproc,unreg,Ref,Na}, got_msg(Pb, gproc)),
  278. Pc = t_spawn_reg(A, Na),
  279. Ref1 = t_call(Pb, {apply, gproc, monitor, [Na]}),
  280. ?assertMatch(true, t_call(Pc, {apply, gproc, unreg, [Na]})),
  281. ?assertMatch({gproc,unreg,Ref1,Na}, got_msg(Pb, gproc)).
  282. t_subscribe([A,B|_] = Ns) ->
  283. Na = ?T_NAME,
  284. Pb = t_spawn(B, _Selective = true),
  285. ?assertEqual(ok, t_call(Pb, {apply, gproc_monitor, subscribe, [Na]})),
  286. ?assertMatch({gproc_monitor, Na, undefined}, got_msg(Pb, gproc_monitor)),
  287. Pa = t_spawn_reg(A, Na),
  288. ?assertMatch({gproc_monitor, Na, Pa}, got_msg(Pb, gproc_monitor)),
  289. Pc = t_spawn(A),
  290. t_call(Pa, {apply, gproc, give_away, [Na, Pc]}),
  291. ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pc)),
  292. ?assertEqual({gproc_monitor,Na,{migrated,Pc}}, got_msg(Pb, gproc_monitor)),
  293. ?assertEqual(ok, t_call(Pc, die)),
  294. ?assertEqual({gproc_monitor,Na,undefined}, got_msg(Pb, gproc_monitor)).
  295. got_msg(Pb, Tag) ->
  296. t_call(Pb,
  297. {apply_fun,
  298. fun() ->
  299. receive
  300. M when element(1, M) == Tag ->
  301. M
  302. after 5000 ->
  303. timeout
  304. end
  305. end}).
  306. %% Verify that the gproc_dist:sync() call returns true even if a candidate dies
  307. %% while the sync is underway. This test makes use of sys:suspend() to ensure that
  308. %% the other candidate doesn't respond too quickly.
  309. t_sync_cand_dies([A,B|_]) ->
  310. Leader = rpc:call(A, gproc_dist, get_leader, []),
  311. Other = case Leader of
  312. A -> B;
  313. B -> A
  314. end,
  315. ?assertMatch(ok, rpc:call(Other, sys, suspend, [gproc_dist])),
  316. P = rpc:call(Other, erlang, whereis, [gproc_dist]),
  317. Key = rpc:async_call(Leader, gproc_dist, sync, []),
  318. %% The overall timeout for gproc_dist:sync() is 5 seconds. Here, we should
  319. %% still be waiting.
  320. ?assertMatch(timeout, rpc:nb_yield(Key, 1000)),
  321. exit(P, kill),
  322. %% The leader should detect that the other candidate died and respond
  323. %% immediately. Therefore, we should have our answer well within 1 sec.
  324. ?assertMatch({value, true}, rpc:nb_yield(Key, 1000)).
  325. t_fail_node([A,B|_] = Ns) ->
  326. Na = ?T_NAME,
  327. Nb = ?T_NAME,
  328. Pa = t_spawn_reg(A, Na),
  329. Pb = t_spawn_reg(B, Nb),
  330. ?assertMatch(ok, rpc:call(A, application, stop, [gproc])),
  331. ?assertMatch(ok, t_lookup_everywhere(Na, Ns -- [A], undefined)),
  332. ?assertMatch(ok, t_lookup_everywhere(Nb, Ns -- [A], Pb)),
  333. ?assertMatch(ok, rpc:call(A, application, start, [gproc])),
  334. ?assertMatch(ok, t_lookup_everywhere(Na, Ns, undefined)),
  335. ?assertMatch(ok, t_lookup_everywhere(Nb, Ns, Pb)),
  336. ?assertMatch(ok, t_call(Pa, die)),
  337. ?assertMatch(ok, t_call(Pb, die)).
  338. t_sleep() ->
  339. timer:sleep(500).
  340. t_lookup_everywhere(Key, Nodes, Exp) ->
  341. t_lookup_everywhere(Key, Nodes, Exp, 3).
  342. t_lookup_everywhere(Key, _, Exp, 0) ->
  343. {lookup_failed, Key, Exp};
  344. t_lookup_everywhere(Key, Nodes, Exp, I) ->
  345. Expected = [{N, Exp} || N <- Nodes],
  346. Found = [{N,rpc:call(N, gproc, where, [Key])} || N <- Nodes],
  347. if Expected =/= Found ->
  348. ?debugFmt("lookup ~p failed~n"
  349. "(Expected: ~p;~n"
  350. " Found : ~p), retrying...~n",
  351. [Key, Expected, Found]),
  352. t_sleep(),
  353. t_lookup_everywhere(Key, Nodes, Exp, I-1);
  354. true ->
  355. ok
  356. end.
  357. t_read_everywhere(Key, Pid, Nodes, Exp) ->
  358. t_read_everywhere(Key, Pid, Nodes, Exp, 3).
  359. t_read_everywhere(Key, _, _, Exp, 0) ->
  360. {read_failed, Key, Exp};
  361. t_read_everywhere(Key, Pid, Nodes, Exp, I) ->
  362. Expected = [{N, Exp} || N <- Nodes],
  363. Found = [{N, read_result(rpc:call(N, gproc, get_value, [Key, Pid]))}
  364. || N <- Nodes],
  365. if Expected =/= Found ->
  366. ?debugFmt("read ~p failed~n"
  367. "(Expected: ~p;~n"
  368. " Found : ~p), retrying...~n",
  369. [{Key, Pid}, Expected, Found]),
  370. t_sleep(),
  371. t_read_everywhere(Key, Pid, Nodes, Exp, I-1);
  372. true ->
  373. ok
  374. end.
  375. read_result({badrpc, {'EXIT', {badarg, _}}}) -> badarg;
  376. read_result(R) -> R.
  377. t_spawn(Node) ->
  378. t_spawn(Node, false).
  379. t_spawn(Node, Selective) when is_boolean(Selective) ->
  380. Me = self(),
  381. P = spawn(Node, fun() ->
  382. Me ! {self(), ok},
  383. t_loop(Selective)
  384. end),
  385. receive
  386. {P, ok} -> P
  387. end.
  388. t_spawn_reg(Node, Name) ->
  389. t_spawn_reg(Node, Name, default_value(Name)).
  390. t_spawn_reg(Node, Name, Value) ->
  391. Me = self(),
  392. spawn(Node, fun() ->
  393. ?assertMatch(true, gproc:reg(Name, Value)),
  394. Me ! {self(), ok},
  395. t_loop()
  396. end),
  397. receive
  398. {P, ok} -> P
  399. end.
  400. t_spawn_reg_shared(Node, Name, Value) ->
  401. Me = self(),
  402. spawn(Node, fun() ->
  403. ?assertMatch(true, gproc:reg_shared(Name, Value)),
  404. Me ! {self(), ok},
  405. t_loop()
  406. end),
  407. receive
  408. {P, ok} -> P
  409. end.
  410. default_value({c,_,_}) -> 0;
  411. default_value(_) -> undefined.
  412. t_spawn_mreg(Node, KVL) ->
  413. Me = self(),
  414. spawn(Node, fun() ->
  415. ?assertMatch(true, gproc:mreg(n, g, KVL)),
  416. Me ! {self(), ok},
  417. t_loop()
  418. end),
  419. receive
  420. {P, ok} -> P
  421. end.
  422. t_call(P, Req) ->
  423. Ref = erlang:monitor(process, P),
  424. P ! {self(), Ref, Req},
  425. receive
  426. {P, Ref, Res} ->
  427. erlang:demonitor(Ref),
  428. Res;
  429. {'DOWN', Ref, _, _, Error} ->
  430. erlang:error({'DOWN', P, Error})
  431. end.
  432. t_loop() ->
  433. t_loop(false).
  434. t_loop(Selective) when is_boolean(Selective) ->
  435. receive
  436. {From, Ref, die} ->
  437. From ! {self(), Ref, ok};
  438. {From, Ref, {selective, Bool}} when is_boolean(Bool) ->
  439. From ! {self(), Ref, ok},
  440. t_loop(Bool);
  441. {From, Ref, {apply, M, F, A}} ->
  442. From ! {self(), Ref, apply(M, F, A)},
  443. t_loop(Selective);
  444. {From, Ref, {apply_fun, F}} ->
  445. From ! {self(), Ref, F()},
  446. t_loop(Selective);
  447. Other when not Selective ->
  448. ?debugFmt("got unknown msg: ~p~n", [Other]),
  449. exit({unknown_msg, Other})
  450. end.
  451. start_slaves(Ns) ->
  452. [H|T] = Nodes = [start_slave(N) || N <- Ns],
  453. _ = [rpc:call(H, net_adm, ping, [N]) || N <- T],
  454. Nodes.
  455. start_slave(Name) ->
  456. case node() of
  457. nonode@nohost ->
  458. os:cmd("epmd -daemon"),
  459. {ok, _} = net_kernel:start([gproc_master, shortnames]);
  460. _ ->
  461. ok
  462. end,
  463. {Pa, Pz} = paths(),
  464. Paths = "-pa ./ -pz ../ebin" ++
  465. lists:flatten([[" -pa " ++ Path || Path <- Pa],
  466. [" -pz " ++ Path || Path <- Pz]]),
  467. {ok, Node} = slave:start(host(), Name, Paths),
  468. %% io:fwrite(user, "Slave node: ~p~n", [Node]),
  469. Node.
  470. paths() ->
  471. Path = code:get_path(),
  472. {ok, [[Root]]} = init:get_argument(root),
  473. {Pas, Rest} = lists:splitwith(fun(P) ->
  474. not lists:prefix(Root, P)
  475. end, Path),
  476. {_, Pzs} = lists:splitwith(fun(P) ->
  477. lists:prefix(Root, P)
  478. end, Rest),
  479. {Pas, Pzs}.
  480. host() ->
  481. [_Name, Host] = re:split(atom_to_list(node()), "@", [{return, list}]),
  482. list_to_atom(Host).
  483. -endif.