@@ -15,7 +15,7 @@ defmodule Hemdal.Host do
1515 If you want to know more about what could be included in a command or
1616 to be run in a host you can review the following modules:
1717
18- - `Hemdal.Config.Alert. Command` where you can check what's included
18+ - `Hemdal.Config.Command` where you can check what's included
1919 inside of the command.
2020 - `Hemdal.Config.Host` where you can find information about the host.
2121
@@ -54,7 +54,7 @@ defmodule Hemdal.Host do
5454 """
5555 use GenServer , restart: :transient
5656 require Logger
57- alias Hemdal.Config.Alert. Command
57+ alias Hemdal.Config.Command
5858 alias :queue , as: Queue
5959
6060 @ default_temporal_dir "/tmp"
@@ -99,6 +99,26 @@ defmodule Hemdal.Host do
9999 @ typedoc false
100100 @ type host_id ( ) :: String . t ( )
101101
102+ @ typedoc """
103+ The options for exec are providing to the execution valid information, and
104+ sometimes required information about the running process.
105+
106+ For example, if we want to run an interactive command but using a
107+ non-background execution, then we will need to provide `caller` option.
108+
109+ In addition, `args` are a list of arguments we pass to a script usually,
110+ and `timeout` makes sense mainly for non-interactive executions. Indeed,
111+ `timeout` is only applicable for the calling of the function, because
112+ it returns usually fast (depending on the loaded queue), it's not very
113+ useful for execution in background mode, and it could be dangerous in
114+ interactive and non-background mode.
115+ """
116+ @ type exec_opts ( ) :: [
117+ timeout: timeout ( ) ,
118+ args: [ String . t ( ) ] ,
119+ caller: pid ( )
120+ ]
121+
102122 @ doc """
103123 Run or execute the command passed as parameter. It's needed to pass the host ID
104124 to find the process where to send the request, and the the command and the
@@ -118,22 +138,24 @@ defmodule Hemdal.Host do
118138 If the JSON sent back from the command is valid, it's usually using that as
119139 data and it's marked as `UNKNOWN` status if there is no status defined.
120140 """
121- @ spec exec ( host_id ( ) , Hemdal.Config.Alert.Command . t ( ) , command_args ( ) ) ::
122- { :ok , map ( ) } | { :error , map ( ) }
123- @ spec exec ( host_id ( ) , Hemdal.Config.Alert.Command . t ( ) , command_args ( ) , timeout ( ) ) ::
124- { :ok , map ( ) } | { :error , map ( ) }
125- def exec ( host_id , cmd , args \\ [ ] , timeout \\ @ timeout_exec )
126-
127- def exec ( host_id , % Command { type: "line" } = cmd , _args , timeout ) do
128- GenServer . call ( via ( host_id ) , { :exec , cmd , [ ] } , timeout )
129- end
130-
131- def exec ( host_id , % Command { type: "script" } = cmd , args , timeout ) do
132- GenServer . call ( via ( host_id ) , { :exec , cmd , args } , timeout )
141+ @ spec exec ( host_id ( ) , Hemdal.Config.Command . t ( ) , exec_opts ( ) ) :: { :ok , map ( ) } | { :error , map ( ) }
142+ def exec ( host_id , cmd , opts \\ [ ] )
143+
144+ def exec ( host_id , % Command { interactive: true } = cmd , opts ) do
145+ if caller = opts [ :caller ] do
146+ timeout = opts [ :timeout ] || @ timeout_exec
147+ args = opts [ :args ] || [ ]
148+ GenServer . call ( via ( host_id ) , { :exec , caller , cmd , args } , timeout )
149+ else
150+ { :error , % { "message" => "Impossible combination" } }
151+ end
133152 end
134153
135- def exec ( host_id , % Command { type: "interactive" } = cmd , [ pid ] , timeout ) do
136- GenServer . call ( via ( host_id ) , { :exec , cmd , [ pid ] } , timeout )
154+ def exec ( host_id , % Command { } = cmd , opts ) do
155+ caller = opts [ :caller ] || self ( )
156+ timeout = opts [ :timeout ] || @ timeout_exec
157+ args = opts [ :args ] || [ ]
158+ GenServer . call ( via ( host_id ) , { :exec , caller , cmd , args } , timeout )
137159 end
138160
139161 @ doc """
@@ -145,10 +167,13 @@ defmodule Hemdal.Host do
145167 which process is in charge of running the background process and to know if it's
146168 still running or not.
147169 """
148- @ spec exec_background ( host_id ( ) , Hemdal.Config.Alert. Command . t ( ) , command_args ( ) ) ::
170+ @ spec exec_background ( host_id ( ) , Hemdal.Config.Command . t ( ) , exec_opts ( ) ) ::
149171 { :ok , pid ( ) } | { :error , any ( ) }
150- def exec_background ( host_id , cmd , args \\ [ ] ) do
151- GenServer . call ( via ( host_id ) , { :exec_background , self ( ) , cmd , args } , @ timeout_exec )
172+ def exec_background ( host_id , cmd , opts \\ [ ] ) do
173+ caller = opts [ :caller ] || self ( )
174+ timeout = opts [ :timeout ] || @ timeout_exec
175+ args = opts [ :args ] || [ ]
176+ GenServer . call ( via ( host_id ) , { :exec_background , caller , cmd , args } , timeout )
152177 end
153178
154179 @ doc """
@@ -248,59 +273,58 @@ defmodule Hemdal.Host do
248273
249274 @ impl GenServer
250275 @ doc false
251- def handle_call ( { :exec_background , pid , cmd , args } , _from , % __MODULE__ { max_workers: :infinity } = state ) do
276+ def handle_call ( { :exec_background , caller , cmd , args } , _from , % __MODULE__ { max_workers: :infinity } = state ) do
252277 Logger . debug ( "host => workers: #{ state . workers } /infinity ; queue length: #{ Queue . len ( state . queue ) } " )
253278
254- send_result = & send ( pid , & 1 )
255- ref = spawn_monitor ( fn -> run_in_background ( cmd , args , send_result , state ) end )
256- { :reply , { :ok , ref } , % __MODULE__ { state | workers: state . workers + 1 } }
279+ send_result = & send ( caller , & 1 )
280+ { worker , _ref } = spawn_monitor ( fn -> run_in_background ( caller , cmd , args , send_result , state ) end )
281+ { :reply , { :ok , worker } , % __MODULE__ { state | workers: state . workers + 1 } }
257282 end
258283
259284 def handle_call (
260- { :exec_background , pid , cmd , args } ,
285+ { :exec_background , caller , cmd , args } ,
261286 from ,
262287 % __MODULE__ { max_workers: max_workers , workers: workers , queue: queue } = state
263288 )
264289 when workers >= max_workers do
265290 Logger . debug ( "host => workers: #{ workers } /#{ max_workers } ; queue length: #{ Queue . len ( queue ) + 1 } " )
266291
267- queue = Queue . in ( { { :exec_background , pid , cmd , args } , from } , queue )
292+ queue = Queue . in ( { { :exec_background , caller , cmd , args } , from } , queue )
268293 { :noreply , % __MODULE__ { state | queue: queue } }
269294 end
270295
271- def handle_call ( { :exec_background , pid , cmd , args } , _from , state ) do
272- send_result = & send ( pid , & 1 )
273- ref = spawn_monitor ( fn -> run_in_background ( cmd , args , send_result , state ) end )
296+ def handle_call ( { :exec_background , caller , cmd , args } , _from , state ) do
297+ send_result = & send ( caller , & 1 )
298+ { worker , _ref } = spawn_monitor ( fn -> run_in_background ( caller , cmd , args , send_result , state ) end )
274299 workers = state . workers + 1
275300
276301 Logger . debug ( "host => workers: #{ workers } /#{ state . max_workers } ; queue length: #{ Queue . len ( state . queue ) } " )
277302
278- { :reply , { :ok , ref } , % __MODULE__ { state | workers: workers } }
303+ { :reply , { :ok , worker } , % __MODULE__ { state | workers: workers } }
279304 end
280305
281- def handle_call ( { :exec , cmd , args } , from , % __MODULE__ { max_workers: :infinity } = state ) do
306+ def handle_call ( { :exec , caller , cmd , args } , from , % __MODULE__ { max_workers: :infinity } = state ) do
282307 Logger . debug ( "host => workers: #{ state . workers } /infinity ; queue length: #{ Queue . len ( state . queue ) } " )
283308
284309 send_result = & GenServer . reply ( from , & 1 )
285- spawn_monitor ( fn -> run_in_background ( cmd , args , send_result , state ) end )
310+ spawn_monitor ( fn -> run_in_background ( caller , cmd , args , send_result , state ) end )
286311 { :noreply , % __MODULE__ { state | workers: state . workers + 1 } }
287312 end
288313
289314 def handle_call (
290- { :exec , cmd , args } ,
315+ { :exec , caller , cmd , args } ,
291316 from ,
292317 % __MODULE__ { max_workers: max_workers , workers: workers , queue: queue } = state
293318 )
294319 when workers >= max_workers do
295320 Logger . debug ( "host => workers: #{ workers } /#{ max_workers } ; queue length: #{ Queue . len ( queue ) + 1 } " )
296-
297- queue = Queue . in ( { { :exec , cmd , args } , from } , queue )
321+ queue = Queue . in ( { { :exec , caller , cmd , args } , from } , queue )
298322 { :noreply , % __MODULE__ { state | queue: queue } }
299323 end
300324
301- def handle_call ( { :exec , cmd , args } , from , state ) do
325+ def handle_call ( { :exec , caller , cmd , args } , from , state ) do
302326 send_result = & GenServer . reply ( from , & 1 )
303- spawn_monitor ( fn -> run_in_background ( cmd , args , send_result , state ) end )
327+ spawn_monitor ( fn -> run_in_background ( caller , cmd , args , send_result , state ) end )
304328 workers = state . workers + 1
305329
306330 Logger . debug ( "host => workers: #{ workers } /#{ state . max_workers } ; queue length: #{ Queue . len ( state . queue ) } " )
@@ -347,20 +371,17 @@ defmodule Hemdal.Host do
347371 @ doc false
348372 def handle_info ( { :DOWN , _ref , :process , _pid , _reason } , state ) do
349373 case Queue . out ( state . queue ) do
350- { :empty , _ } ->
374+ { :empty , queue } ->
351375 workers = state . workers - 1
352376 Logger . debug ( "host => workers: #{ workers } /#{ state . max_workers } ; queue length: 0" )
353- { :noreply , % __MODULE__ { state | workers: workers } }
377+ { :noreply , % __MODULE__ { state | workers: workers , queue: queue } }
354378
355- { { :value , { { :exec , cmd , args } , from } } , queue } ->
379+ { { :value , { { :exec , caller , cmd , args } , from } } , queue } ->
356380 state = % __MODULE__ { state | queue: queue }
357381 send_result = & GenServer . reply ( from , & 1 )
358- spawn_monitor ( fn -> run_in_background ( cmd , args , send_result , state ) end )
359-
360- Logger . debug (
361- "host => workers: #{ state . workers } /#{ state . max_workers } ; queue length: #{ Queue . len ( queue ) } "
362- )
363-
382+ spawn_monitor ( fn -> run_in_background ( caller , cmd , args , send_result , state ) end )
383+ qlen = Queue . len ( queue )
384+ Logger . debug ( "host => workers: #{ state . workers } /#{ state . max_workers } ; queue length: #{ qlen } " )
364385 { :noreply , state }
365386 end
366387 end
@@ -393,9 +414,9 @@ defmodule Hemdal.Host do
393414 { :error , % { "message" => other , "status" => "FAIL" } }
394415 end
395416
396- defp run_in_background ( cmd , args , send_result , % __MODULE__ { host: % _ { module: mod } = host } ) do
417+ defp run_in_background ( caller , cmd , args , send_result , % __MODULE__ { host: % _ { module: mod } = host } ) do
397418 mod . transaction ( host , fn handler ->
398- with { :ok , errorlevel , output } <- exec_cmd ( handler , mod , cmd , args ) ,
419+ with { :ok , errorlevel , output } <- exec_cmd ( handler , mod , caller , cmd , args ) ,
399420 { :ok , % { "status" => status } = data } <- decode ( output ) do
400421 Logger . debug ( "command exit(#{ errorlevel } ) output: #{ inspect ( data ) } " )
401422 run_result ( data , errorlevel , status )
@@ -432,11 +453,15 @@ defmodule Hemdal.Host do
432453 |> String . pad_leading ( 7 , "0" )
433454 end
434455
435- defp exec_cmd ( handler , mod , % Command { type: "line" , command: command } , _args ) do
456+ defp exec_cmd ( handler , mod , _caller , % Command { type: "line" , command: command , interactive: false } , _args ) do
436457 mod . exec ( handler , command )
437458 end
438459
439- defp exec_cmd ( handler , mod , % Command { type: "script" , command: script } , args ) do
460+ defp exec_cmd ( handler , mod , caller , % Command { type: "line" , command: command } , _args ) do
461+ mod . exec_interactive ( handler , command , caller )
462+ end
463+
464+ defp exec_cmd ( handler , mod , _caller , % Command { type: "script" , command: script , interactive: false } , args ) do
440465 tmp_file = Path . join ( [ @ default_temporal_dir , random_string ( ) ] )
441466
442467 try do
@@ -454,8 +479,18 @@ defmodule Hemdal.Host do
454479 end
455480 end
456481
457- defp exec_cmd ( handler , mod , % Command { type: "interactive" , command: command } , [ pid ] ) do
458- mod . exec_interactive ( handler , command , pid )
482+ defp exec_cmd ( handler , mod , caller , % Command { type: "script" , command: script } , args ) do
483+ tmp_file = Path . join ( [ @ default_temporal_dir , random_string ( ) ] )
484+
485+ sh =
486+ case String . split ( script , [ "\n " ] , trim: true ) do
487+ [ "#!" <> shell | _ ] -> shell
488+ _ -> @ default_shell
489+ end
490+
491+ mod . write_file ( handler , tmp_file , script )
492+ cmd = Enum . join ( [ sh , tmp_file | args ] , " " )
493+ mod . exec_interactive ( handler , cmd , caller )
459494 end
460495
461496 @ typedoc """
0 commit comments