aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/user_sup.erl
blob: 35b7ff0cfea883422c7165ea5e8c9b78e243a5db (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%%
-module(user_sup).

%% ---------------------------------------------
%% This is a supervisor bridge hiding the process
%% details of the user/group implementation.
%% ---------------------------------------------

-behaviour(supervisor_bridge).

-export([start/0]).

%% Internal exports.
-export([init/1, terminate/2, relay/1]).

-spec start() -> {'error', {'already_started', pid()}} | {'ok', pid()}.

start() ->
    supervisor_bridge:start_link(user_sup, []).

-spec init([]) -> 'ignore' | {'error', 'nouser'} | {'ok', pid(), pid()}.

init([]) ->
    case get_user() of
	nouser ->
	    ignore;
	{master, Master} ->
	    Pid = start_slave(Master),
	    {ok, Pid, Pid};
	{M, F, A} ->
	    case start_user({M, F}, A) of
		{ok, Pid} ->
		    {ok, Pid, Pid};
		Error ->
		    Error
	    end
    end.

start_slave(Master) ->
    case rpc:call(Master, erlang, whereis, [user]) of
	User when is_pid(User) ->
	    spawn(?MODULE, relay, [User]);
	_ ->
	    error_logger:error_msg("Cannot get remote user", []),
	    receive after 1000 -> true end,
	    halt()
    end.

-spec relay(pid()) -> no_return().

relay(Pid) ->
    register(user, self()),
    relay1(Pid).

relay1(Pid) ->
    receive
        X ->
            Pid ! X,
	    relay1(Pid)
    end.


%%-----------------------------------------------------------------
%% Sleep a while in order to let user write all (some) buffered 
%% information before termination.
%%-----------------------------------------------------------------

-spec terminate(term(), pid()) -> 'ok'.

terminate(_Reason, UserPid) ->
    receive after 1000 -> ok end,
    exit(UserPid, kill),
    ok.

%%-----------------------------------------------------------------
%% If there is a user, wait for it to register itself.  (But wait
%% no more than 10 seconds).  This is so the application_controller
%% is guaranteed that the user is started.
%%-----------------------------------------------------------------

start_user(Func,A) ->
    apply(Func, A),
    wait_for_user_p(100).

wait_for_user_p(0) ->
    {error, nouser};
wait_for_user_p(N) ->
    case whereis(user) of
	Pid when is_pid(Pid) ->
	    link(Pid),
	    {ok, Pid};
	_ ->
	    receive after 100 -> ok end,
	    wait_for_user_p(N-1)
    end.

get_user() ->
    Flags = init:get_arguments(),
    check_flags(Flags, {user_drv, start, []}).

%% These flags depend upon what arguments the erl script passes on
%% to erl91.
check_flags([{nouser, []} |T], _) -> check_flags(T, nouser);
check_flags([{user, [User]} | T], _) ->
    check_flags(T, {list_to_atom(User), start, []});
check_flags([{noshell, []} | T], _) -> check_flags(T, {user, start, []});
check_flags([{oldshell, []} | T], _) -> check_flags(T, {user, start, []});
check_flags([{noinput, []} | T], _) -> check_flags(T, {user, start_out, []});
check_flags([{master, [Node]} | T], _) ->
    check_flags(T, {master, list_to_atom(Node)});
check_flags([_H | T], User) -> check_flags(T, User);
check_flags([], User) -> User.