~ubuntu-branches/ubuntu/wily/marionnet/wily

« back to all changes in this revision

Viewing changes to daemon_language.ml

  • Committer: Package Import Robot
  • Author(s): Lucas Nussbaum
  • Date: 2013-03-29 15:57:12 UTC
  • Revision ID: package-import@ubuntu.com-20130329155712-o0b9b96w8av68ktq
Tags: upstream-0.90.6+bzr407
Import upstream version 0.90.6+bzr407

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(* This file is part of Marionnet, a virtual network laboratory
 
2
   Copyright (C) 2008  Luca Saiu
 
3
   Copyright (C) 2010  Jean-Vincent Loddo
 
4
   Copyright (C) 2008, 2010  Université Paris 13
 
5
 
 
6
   This program is free software: you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation, either version 2 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program 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
 
14
   GNU General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU General Public License
 
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>. *)
 
18
 
 
19
 
 
20
(** The Marionnet daemon is controlled by a simple command language. Messages
 
21
    are passed as strings over sockets, which are printed from [parsed to]
 
22
    very simple abstract syntax terms. *)
 
23
 
 
24
(** Tap names and bridge names are just strings: *)
 
25
type tap_name =
 
26
    string;;
 
27
type bridge_name =
 
28
    string;;
 
29
type ip_address =
 
30
    string;;
 
31
type uid =
 
32
    int;;
 
33
 
 
34
(** The abstract syntax of requests, responses and parameters: *)
 
35
type resource_pattern =
 
36
  | AnyTap of uid * ip_address
 
37
  | AnySocketTap of uid * bridge_name
 
38
type resource =
 
39
  | Tap of tap_name
 
40
  | SocketTap of tap_name * uid * bridge_name
 
41
and daemon_request =
 
42
  | IAmAlive
 
43
  | Make of resource_pattern
 
44
  | Destroy of resource
 
45
  | DestroyAllMyResources
 
46
and daemon_response =
 
47
  | Success
 
48
  | Error of string
 
49
  | Created of resource
 
50
  | SorryIThoughtYouWereDead;;
 
51
 
 
52
(** Printer: this is useful for debugging. *)
 
53
let rec string_of_daemon_resource resource =
 
54
  match resource with
 
55
  | Tap tap_name ->
 
56
      Printf.sprintf "(tap %s)" tap_name
 
57
  | SocketTap(tap_name, uid, bridge_name) ->
 
58
      Printf.sprintf "(socket-tap %s %i %s)" tap_name uid bridge_name
 
59
let rec string_of_daemon_resource_pattern resource_pattern =
 
60
  match resource_pattern with
 
61
  | AnyTap(uid, ip_address) ->
 
62
      Printf.sprintf "(any-tap %i %s)" uid ip_address
 
63
  | AnySocketTap(uid, bridge_name) ->
 
64
      Printf.sprintf "(any-socket-tap %i %s)" uid bridge_name
 
65
and string_of_daemon_request request =
 
66
  match request with
 
67
  | IAmAlive ->
 
68
      "i-am-alive"
 
69
  | Make resource_pattern ->
 
70
      Printf.sprintf "(make %s)" (string_of_daemon_resource_pattern resource_pattern)
 
71
  | Destroy resource ->
 
72
      Printf.sprintf "(destroy %s)" (string_of_daemon_resource resource)
 
73
  | DestroyAllMyResources ->
 
74
      "destroy-all-my-resources"
 
75
and string_of_daemon_response response =
 
76
  match response with
 
77
  | Success ->
 
78
      "success"
 
79
  | SorryIThoughtYouWereDead ->
 
80
      "sorry-i-thought-you-were-dead"
 
81
  | Error message ->
 
82
      Printf.sprintf "(error \"%s\")" message
 
83
  | Created resource ->
 
84
      Printf.sprintf "(created %s)" (string_of_daemon_resource resource);;
 
85
 
 
86
(** The length of all requests and responses in our protocol: *)
 
87
let message_length = 128;;
 
88
 
 
89
(** Return a fixed-length string of exactly message_length bytes, where the first
 
90
    character is the given opcode, the following characters are the given parameters,
 
91
    and the remaining characters, if any, are filled with spaces. The length of the
 
92
    parameter is checked: *)
 
93
let make_fixed_length_message opcode parameter =
 
94
  let parameter =
 
95
    if ((String.length parameter) + 1) > message_length then begin
 
96
      Log.printf "Warning: the parameter \"%s\" is too long. Truncating...\n" parameter;
 
97
      flush_all ();
 
98
      String.sub parameter 0 ((String.length parameter) - 1)
 
99
    end else
 
100
      parameter in
 
101
  (Printf.sprintf "%c" opcode) ^
 
102
  parameter ^
 
103
  (String.make (message_length - (String.length parameter) - 1) ' ');;
 
104
 
 
105
(** Request printer (this is for the actually communication language, not for
 
106
    debugging): *)
 
107
let print_request request =
 
108
  match request with
 
109
  | IAmAlive ->
 
110
      make_fixed_length_message 'i' ""
 
111
  | Make AnyTap(uid, ip_address) ->
 
112
      make_fixed_length_message 'c' (Printf.sprintf "%i %s" uid ip_address)
 
113
  | Make (AnySocketTap(uid, bridge_name)) ->
 
114
      make_fixed_length_message 'g' (Printf.sprintf "%i %s" uid bridge_name)
 
115
  | Destroy (Tap tap_name) ->
 
116
      make_fixed_length_message 'd' tap_name
 
117
  | Destroy (SocketTap(tap_name, uid, bridge_name)) ->
 
118
      make_fixed_length_message 'D' (Printf.sprintf "%s %i %s" tap_name uid bridge_name)
 
119
  | DestroyAllMyResources ->
 
120
      make_fixed_length_message '!' "";;
 
121
 
 
122
(** Response printer (this is for the actually communication language, not for
 
123
    debugging): *)
 
124
let print_response response =
 
125
  match response with
 
126
  | Success ->
 
127
      make_fixed_length_message 's' ""
 
128
  | Error message ->
 
129
      make_fixed_length_message 'e' message
 
130
  | Created (Tap tap_name) ->
 
131
      make_fixed_length_message 'c' tap_name
 
132
  | Created (SocketTap(tap_name, uid, bridge_name)) ->
 
133
      make_fixed_length_message
 
134
        'C'
 
135
        (Printf.sprintf "%s %i %s" tap_name uid bridge_name)
 
136
  | SorryIThoughtYouWereDead ->
 
137
      make_fixed_length_message '!' "";;
 
138
 
 
139
let remove_trailing_spaces string =
 
140
  let rec index_of_the_last_nonblank string index =
 
141
    (* We return -1 if the string is completely made of spaces. This is
 
142
       coherent with the way we use this local funcion below. *)
 
143
    if index = -1 then
 
144
      -1
 
145
    else if String.get string index = ' ' then
 
146
      index_of_the_last_nonblank string (index - 1)
 
147
    else
 
148
      index
 
149
  in
 
150
  String.sub
 
151
    string
 
152
    0
 
153
    (1 + (index_of_the_last_nonblank string ((String.length string) - 1)));;
 
154
 
 
155
(** Return the opcode and parameter of the given message: *)
 
156
let split_message message =
 
157
  assert((String.length message) = message_length);
 
158
  let opcode = String.get message 0 in
 
159
  let rest = String.sub message 1 (message_length - 1) in
 
160
  let parameter = remove_trailing_spaces rest in
 
161
  opcode, parameter;;
 
162
 
 
163
let parse_request request =
 
164
  let (opcode, parameter) = split_message request in
 
165
  match opcode with
 
166
  | 'i' ->
 
167
      IAmAlive
 
168
  | 'c' ->
 
169
      Scanf.sscanf parameter "%i %s" (fun uid ip_address -> Make (AnyTap(uid, ip_address)))
 
170
  | 'g' ->
 
171
      Scanf.sscanf parameter "%i %s" (fun uid bridge_name -> Make (AnySocketTap(uid, bridge_name)))
 
172
  | 'd' ->
 
173
      Destroy (Tap parameter)
 
174
  | 'D' ->
 
175
      Scanf.sscanf
 
176
        parameter
 
177
        "%s %i %s"
 
178
        (fun tap_name uid bridge_name ->
 
179
          Destroy (SocketTap(tap_name, uid, bridge_name)))
 
180
  | '!' ->
 
181
      DestroyAllMyResources
 
182
  | _ ->
 
183
      failwith ("Could not parse the request \"" ^ request ^ "\"");;
 
184
 
 
185
let parse_response response  =
 
186
  let (opcode, parameter) = split_message response in
 
187
  match opcode with
 
188
  | 's' -> Success
 
189
  | 'e' -> Error parameter
 
190
  | 'c' -> Created (Tap parameter)
 
191
  | 'C' ->
 
192
      Scanf.sscanf
 
193
        parameter
 
194
        "%s %i %s"
 
195
        (fun tap_name uid bridge_name ->
 
196
          Created (SocketTap(tap_name, uid, bridge_name)))
 
197
  | '!' -> SorryIThoughtYouWereDead
 
198
  | _ -> failwith ("Could not parse the response \"" ^ response ^ "\"");;
 
199
 
 
200
(** We need to handle SIGPIPE when working with sockets, as a SIGPIPE
 
201
    is the visible effect of an interrupted primitive at the OCaml level.
 
202
    Not doing this leads to extremely nasty bugs, very hard to reproduce.
 
203
    This may not the "correct" module to implement this, but in this way
 
204
    I'm sure that every process, both Marionnet (client) and the daemon
 
205
    (server) always handle the signal. *)
 
206
let signal_handler =
 
207
  fun signal ->
 
208
    Log.printf "=========================\n";
 
209
    Log.printf "I received the signal %i!\n" signal;
 
210
    Log.printf "=========================\n";
 
211
    flush_all ();
 
212
    (* Raise an exception instead of silently killing a process... *)
 
213
    failwith (Printf.sprintf "got the signal %i" signal);;
 
214
Sys.set_signal Sys.sigpipe (Sys.Signal_handle signal_handler);;