1
(* $Id: netsys.ml 1121 2007-05-06 18:20:37Z gerd $ *)
5
let rec restart f arg =
9
| Unix.Unix_error(Unix.EINTR,_,_) ->
1
(* $Id: netsys.ml 1662 2011-08-29 23:05:06Z gerd $ *)
9
let dlog = Netlog.Debug.mk_dlog "Netsys" Debug.enable
10
let dlogr = Netlog.Debug.mk_dlogr "Netsys" Debug.enable
13
Netlog.Debug.register_module "Netsys" Debug.enable
16
exception Shutdown_not_supported
21
external netsys_is_darwin : unit -> bool = "netsys_is_darwin"
26
external int64_of_file_descr : Unix.file_descr -> int64
27
= "netsys_int64_of_file_descr"
28
(* Also occurs in netsys_win32.ml! *)
36
let is_absolute path =
38
(String.length path >= 3 &&
41
(path.[2] = '/' || path.[2] = '\\')
43
(String.length path >= 2 &&
44
(path.[0] = '/' || path.[0] = '\\') &&
45
(path.[1] = '/' || path.[1] = '\\')
48
path <> "" && path.[0] = '/'
51
let restart = Netsys_impl_util.restart
52
let restart_tmo = Netsys_impl_util.restart_tmo
12
54
let restarting_select fd_rd fd_wr fd_oob tmo =
13
let t0 = Unix.gettimeofday() in
15
let rec tryagain t_elapsed =
16
let tmo' = tmo -. t_elapsed in
19
Unix.select fd_rd fd_wr fd_oob tmo'
21
| Unix.Unix_error(Unix.EINTR,_,_) ->
22
let t1 = Unix.gettimeofday() in
31
restart (Unix.select fd_rd fd_wr fd_oob) tmo
34
let rec really_write fd s pos len =
37
let n = Unix.single_write fd s pos len in
38
really_write fd s (pos+n) (len-n)
40
| Unix.Unix_error(Unix.EINTR, _, _) ->
41
really_write fd s pos len
42
| Unix.Unix_error( (Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) ->
43
ignore(restart (Unix.select [] [fd] []) (-1.0));
44
really_write fd s pos len
49
let blocking_read fd s pos len =
55
restart_tmo (Unix.select fd_rd fd_wr fd_oob) tmo
59
if is_win32 then Netsys_win32.real_select else Unix.select in
64
let restarting_sleep t =
72
| Unix.Unix_error(Unix.EINVAL,a1,a2) ->
73
(* SUS defines EINVAL as "socket has been shut down". This is a bit
74
* surprising for developers of Open Source OS where this is reported
75
* as ENOTCONN. We map it here.
77
raise(Unix.Unix_error(Unix.ENOTCONN,a1,a2))
82
| `Recv_send of Unix.sockaddr * Unix.sockaddr
96
try Some(Netsys_win32.lookup fd)
97
with Not_found -> None in
98
match w32_obj_opt with
99
| Some (Netsys_win32.W32_pipe _) ->
101
| Some (Netsys_win32.W32_pipe_server _) ->
103
| Some (Netsys_win32.W32_event _) ->
105
| Some (Netsys_win32.W32_process _) ->
107
| Some (Netsys_win32.W32_input_thread _) ->
109
| Some (Netsys_win32.W32_output_thread _) ->
112
(* Check whether we have a socket or not: *)
114
let _socktype = Unix.getsockopt_int fd Unix.SO_TYPE in
115
(* Now check whether the socket is connected or not: *)
117
let sockaddr = Unix.getsockname fd in
118
let peeraddr = getpeername fd in
119
(* fd is a connected socket *)
120
`Recv_send(sockaddr,peeraddr)
122
| Unix.Unix_error(Unix.ENOTCONN,_,_) ->
123
(* fd is an unconnected socket *)
125
| Unix.Unix_error(Unix.ENOTSOCK,_,_) ->
126
failwith "Got unexpected ENOTSOCK" (* hopefully we never see this *)
128
(* There are various error codes in use for socket types that
129
do not use addresses, e.g. socketpairs are considered
130
as not having addresses by some OS. Common are
131
EAFNOSUPPORT, EOPNOTSUPP, EINVAL. For simplicity we catch
132
here all, which is allowed as we already know that fd is a
137
| Unix.Unix_error((Unix.ENOTSOCK|Unix.EINVAL),_,_) ->
138
(* Note: EINVAL is used by some oldish OS in this case *)
139
(* fd is not a socket *)
141
| Unix.Unix_error((Unix.ENOENT,_,_)) when is_win32 ->
145
("get_fd_style: Exception: " ^ Netexn.to_string e);
148
let string_of_sockaddr =
150
| Unix.ADDR_INET(inet,port) as addr ->
151
( match Unix.domain_of_sockaddr addr with
153
Unix.string_of_inet_addr inet ^ ":" ^ string_of_int port
155
"[" ^ Unix.string_of_inet_addr inet ^ "]:" ^ string_of_int port
159
| Unix.ADDR_UNIX path ->
162
let string_of_fd_style =
164
| `Read_write -> "Read_write"
165
| `Recv_send (sockaddr,peeraddr) ->
166
"Recv_send(" ^ string_of_sockaddr sockaddr ^ "," ^
167
string_of_sockaddr peeraddr ^ ")"
168
| `Recv_send_implied -> "Recv_send_implied"
169
| `Recvfrom_sendto -> "Recvfrom_sendto"
170
| `W32_pipe -> "W32_pipe"
171
| `W32_pipe_server -> "W32_pipe_server"
172
| `W32_event -> "W32_event"
173
| `W32_process -> "W32_process"
174
| `W32_input_thread -> "W32_input_thread"
175
| `W32_output_thread -> "W32_output_thread"
177
let string_of_fd fd =
178
let st = get_fd_style fd in
179
let fdi = int64_of_file_descr fd in
182
sprintf "fd<%Ld>" fdi
183
| `Recv_send(sockaddr,peeraddr) ->
184
sprintf "fd<%Ld=socket(%s,%s)>"
185
fdi (string_of_sockaddr sockaddr) (string_of_sockaddr peeraddr)
186
| `Recv_send_implied ->
187
sprintf "fd<%Ld=socket>" fdi
188
| `Recvfrom_sendto ->
189
sprintf "fd<%Ld=socket>" fdi
191
let p = Netsys_win32.lookup_pipe fd in
192
sprintf "fd<%Ld=w32_pipe(%s)>" fdi (Netsys_win32.pipe_name p)
193
| `W32_pipe_server ->
194
let p = Netsys_win32.lookup_pipe_server fd in
195
sprintf "fd<%Ld=w32_pipe_server(%s)>"
196
fdi (Netsys_win32.pipe_server_name p)
198
sprintf "fd<%Ld=w32_event>" fdi
200
let p = Netsys_win32.lookup_process fd in
201
sprintf "fd<%Ld=w32_process(%d)>" fdi (Netsys_win32.win_pid p)
202
| `W32_input_thread ->
203
sprintf "fd<%Ld=w32_input_thread>" fdi
204
| `W32_output_thread ->
205
sprintf "fd<%Ld=w32_output_thread>" fdi
208
let wait_until_readable fd_style fd tmo =
209
dlogr (fun () -> sprintf "wait_until_readable fd=%Ld tmo=%f"
210
(int64_of_file_descr fd) tmo);
211
if Netsys_posix.have_poll() then
213
(Netsys_posix.poll_single fd true false false) tmo
216
| `Read_write when is_win32 -> (* effectively not supported! *)
219
let ph = Netsys_win32.lookup_pipe fd in
220
Netsys_win32.pipe_wait_rd ph tmo
221
| `W32_pipe_server ->
222
let ph = Netsys_win32.lookup_pipe_server fd in
223
Netsys_win32.pipe_wait_connect ph tmo
225
let eo = Netsys_win32.lookup_event fd in
226
Netsys_win32.event_wait eo tmo
227
| `W32_input_thread ->
228
let ithr = Netsys_win32.lookup_input_thread fd in
229
let eo = Netsys_win32.input_thread_event ithr in
230
Netsys_win32.event_wait eo tmo
232
| `W32_output_thread ->
233
sleep tmo; false (* never *)
235
let l,_,_ = restart_tmo (Unix.select [fd] [] []) tmo in
238
let wait_until_writable fd_style fd tmo =
239
dlogr (fun () -> sprintf "wait_until_writable fd=%Ld tmo=%f"
240
(int64_of_file_descr fd) tmo);
241
if Netsys_posix.have_poll() then
243
(Netsys_posix.poll_single fd false true false) tmo
246
| `Read_write when is_win32 -> (* effectively not supported! *)
249
let ph = Netsys_win32.lookup_pipe fd in
250
Netsys_win32.pipe_wait_wr ph tmo
251
| `W32_pipe_server ->
252
let ph = Netsys_win32.lookup_pipe_server fd in
253
Netsys_win32.pipe_wait_connect ph tmo
255
let eo = Netsys_win32.lookup_event fd in
256
Netsys_win32.event_wait eo tmo
257
| `W32_output_thread ->
258
let othr = Netsys_win32.lookup_output_thread fd in
259
let eo = Netsys_win32.output_thread_event othr in
260
Netsys_win32.event_wait eo tmo
263
sleep tmo; false (* never *)
265
let _,l,_ = restart_tmo (Unix.select [] [fd] []) tmo in
268
let wait_until_prird fd_style fd tmo =
269
dlogr (fun () -> sprintf "wait_until_prird fd=%Ld tmo=%f"
270
(int64_of_file_descr fd) tmo);
271
if Netsys_posix.have_poll() then
273
(Netsys_posix.poll_single fd false false true) tmo
276
| `Read_write when is_win32 -> (* effectively not supported! *)
279
sleep tmo; false (* never *)
280
| `W32_pipe_server ->
281
let ph = Netsys_win32.lookup_pipe_server fd in
282
Netsys_win32.pipe_wait_connect ph tmo
284
let eo = Netsys_win32.lookup_event fd in
285
Netsys_win32.event_wait eo tmo
289
sleep tmo; false (* never *)
291
let _,_,l = restart_tmo (Unix.select [] [] [fd]) tmo in
295
let is_readable fd_style fd = wait_until_readable fd_style fd 0.0
296
let is_writable fd_style fd = wait_until_writable fd_style fd 0.0
297
let is_prird fd_style fd = wait_until_prird fd_style fd 0.0
300
let gwrite fd_style fd s pos len =
301
dlogr (fun () -> sprintf "gwrite fd=%Ld len=%d"
302
(int64_of_file_descr fd) len);
305
Unix.single_write fd s pos len
307
| `Recv_send_implied ->
308
Unix.send fd s pos len []
309
| `Recvfrom_sendto ->
310
failwith "Netsys.gwrite: the socket is unconnected"
312
let ph = Netsys_win32.lookup_pipe fd in
313
Netsys_win32.pipe_write ph s pos len
314
| `W32_pipe_server ->
315
failwith "Netsys.gwrite: cannot write to pipe servers"
317
failwith "Netsys.gwrite: cannot write to event descriptor"
319
failwith "Netsys.gwrite: cannot write to process descriptor"
320
| `W32_input_thread ->
321
failwith "Netsys.gwrite: cannot write to input thread"
322
| `W32_output_thread ->
323
let othr = Netsys_win32.lookup_output_thread fd in
324
Netsys_win32.output_thread_write othr s pos len
327
let rec really_gwrite fd_style fd s pos len =
329
let n = gwrite fd_style fd s pos len in
331
really_gwrite fd_style fd s (pos+n) (len-n)
333
| Unix.Unix_error(Unix.EINTR, _, _) ->
334
really_gwrite fd_style fd s pos len
335
| Unix.Unix_error( (Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) ->
336
ignore(wait_until_writable fd_style fd (-1.0));
337
really_gwrite fd_style fd s pos len
340
let gread fd_style fd s pos len =
341
dlogr (fun () -> sprintf "gread fd=%Ld len=%d"
342
(int64_of_file_descr fd) len);
345
Unix.read fd s pos len
347
| `Recv_send_implied ->
348
Unix.recv fd s pos len []
349
| `Recvfrom_sendto ->
350
failwith "Netsys.gread: the socket is unconnected"
352
let ph = Netsys_win32.lookup_pipe fd in
353
Netsys_win32.pipe_read ph s pos len
354
| `W32_pipe_server ->
355
failwith "Netsys.gwrite: cannot read from pipe servers"
357
failwith "Netsys.gread: cannot read from event descriptor"
359
failwith "Netsys.gread: cannot read from process descriptor"
360
| `W32_output_thread ->
361
failwith "Netsys.gread: cannot read from output thread"
362
| `W32_input_thread ->
363
let ithr = Netsys_win32.lookup_input_thread fd in
364
Netsys_win32.input_thread_read ithr s pos len
366
let blocking_gread fd_style fd s pos len =
50
367
let rec loop pos len p =
53
let n = Unix.read fd s pos len in
370
let n = gread fd_style fd s pos len in
70
let really_read fd s pos len =
72
let p = blocking_read fd s pos len in
73
if p < len then raise End_of_file;
387
let really_gread fd_style fd s pos len =
388
let p = blocking_gread fd_style fd s pos len in
389
if p < len then raise End_of_file;
393
let wait_until_connected fd tmo =
394
dlogr (fun () -> sprintf "wait_until_connected fd=%Ld tmo=%f"
395
(int64_of_file_descr fd) tmo);
398
let w32 = Netsys_win32.lookup fd in
400
| Netsys_win32.W32_pipe _ -> true (* immediately connected *)
402
failwith "Netsys.wait_until_connected: bad descriptor type"
405
| Not_found -> (* socket case *)
406
let l1,_,l2 = Netsys_win32.real_select [] [fd] [fd] tmo in
409
wait_until_writable `Recv_send fd tmo
412
let catch_exn label getdetail f arg =
417
let detail = getdetail arg in
418
( try (* be careful here, logging might not work *)
420
"%s (%s): Exception %s"
421
label detail (Netexn.to_string error)
426
let is_std fd std_fd std_num =
428
Netsys_win32.is_crt_fd fd std_num
432
let is_stdin fd = is_std fd Unix.stdin 0
433
let is_stdout fd = is_std fd Unix.stdout 1
434
let is_stderr fd = is_std fd Unix.stderr 2
436
let set_close_on_exec fd =
438
Netsys_win32.modify_close_on_exec fd true
440
Unix.set_close_on_exec fd
443
let clear_close_on_exec fd =
445
Netsys_win32.modify_close_on_exec fd false
447
Unix.clear_close_on_exec fd
450
let gshutdown fd_style fd cmd =
451
dlogr (fun () -> sprintf "gshutdown fd=%Ld cmd=%s"
452
(int64_of_file_descr fd)
454
| Unix.SHUTDOWN_SEND -> "SEND"
455
| Unix.SHUTDOWN_RECEIVE -> "RECEIVE"
456
| Unix.SHUTDOWN_ALL -> "ALL"
461
| `Recv_send_implied ->
465
| Unix.Unix_error(Unix.ENOTCONN, _, _) -> ()
468
if cmd <> Unix.SHUTDOWN_ALL then
469
raise(Unix.Unix_error(Unix.EPERM, "Netsys.gshutdown", ""));
470
let p = Netsys_win32.lookup_pipe fd in
471
Netsys_win32.pipe_shutdown p
472
| `W32_pipe_server ->
473
if cmd <> Unix.SHUTDOWN_ALL then
474
raise(Unix.Unix_error(Unix.EPERM, "Netsys.gshutdown", ""));
475
let p = Netsys_win32.lookup_pipe_server fd in
476
Netsys_win32.pipe_shutdown_server p
477
| `W32_output_thread ->
478
if cmd <> Unix.SHUTDOWN_RECEIVE then (
479
let othr = Netsys_win32.lookup_output_thread fd in
480
Netsys_win32.close_output_thread othr
483
raise Shutdown_not_supported
486
let gclose fd_style fd =
487
dlogr (fun () -> sprintf "gclose fd=%Ld" (int64_of_file_descr fd));
489
Printf.sprintf "fd %Ld" (int64_of_file_descr fd) in
490
let pipe_detail (fd,p) =
491
Printf.sprintf "fd %Ld as pipe %s"
492
(int64_of_file_descr fd)
493
(Netsys_win32.pipe_name p) in
494
let psrv_detail (fd,p) =
495
Printf.sprintf "fd %Ld as pipe server %s"
496
(int64_of_file_descr fd)
497
(Netsys_win32.pipe_server_name p) in
498
let ithr_detail (fd,p) =
499
Printf.sprintf "fd %Ld as input thread for %Ld"
500
(int64_of_file_descr fd)
501
(int64_of_file_descr(Netsys_win32.input_thread_descr p)) in
502
let othr_detail (fd,p) =
503
Printf.sprintf "fd %Ld as output thread for %Ld"
504
(int64_of_file_descr fd)
505
(int64_of_file_descr(Netsys_win32.output_thread_descr p)) in
508
| `Recvfrom_sendto ->
510
"Unix.close" fd_detail
513
| `Recv_send_implied ->
515
"Unix.shutdown" fd_detail
518
Unix.shutdown fd Unix.SHUTDOWN_ALL
520
| Unix.Unix_error(Unix.ENOTCONN, _, _) -> ()
524
"Unix.close" fd_detail
527
let p = Netsys_win32.lookup_pipe fd in
529
"Netsys_win32.pipe_shutdown" pipe_detail
530
(fun (fd,p) -> Netsys_win32.pipe_shutdown p)
533
"Unix.close" fd_detail
535
Netsys_win32.unregister fd
536
| `W32_pipe_server ->
537
let p = Netsys_win32.lookup_pipe_server fd in
539
"Netsys_win32.pipe_server_shutdown" psrv_detail
540
(fun (fd,p) -> Netsys_win32.pipe_shutdown_server p)
543
"Unix.close" fd_detail
545
Netsys_win32.unregister fd
546
| `W32_event | `W32_process ->
547
(* Events are automatically closed *)
549
"Unix.close" fd_detail
551
Netsys_win32.unregister fd
552
| `W32_input_thread ->
553
let ithr = Netsys_win32.lookup_input_thread fd in
555
"Netsys_win32.cancel_input_thread" ithr_detail
556
(fun (fd,ithr) -> Netsys_win32.cancel_input_thread ithr)
559
"Unix.close" fd_detail
561
Netsys_win32.unregister fd
562
| `W32_output_thread ->
563
let othr = Netsys_win32.lookup_output_thread fd in
565
"Netsys_win32.cancel_output_thread" othr_detail
566
(fun (fd,othr) -> Netsys_win32.cancel_output_thread othr)
569
"Unix.close" fd_detail
571
Netsys_win32.unregister fd
575
external unix_error_of_code : int -> Unix.error = "netsys_unix_error_of_code"
578
let connect_check fd =
582
let w32 = Netsys_win32.lookup fd in
584
| Netsys_win32.W32_pipe _ -> false (* immediately connected *)
586
failwith "Netsys.connect_check: bad descriptor type"
589
| Not_found -> (* socket case *)
594
let e_code = Unix.getsockopt_int fd Unix.SO_ERROR in
596
ignore(getpeername fd);
599
| Unix.Unix_error(Unix.ENOTCONN,_,_) ->
602
let own_addr = Unix.getsockname fd in
603
string_of_sockaddr own_addr
605
raise(Unix.Unix_error(unix_error_of_code e_code,
606
"connect_check", detail))
79
611
let domain_of_inet_addr addr =
80
612
Unix.domain_of_sockaddr(Unix.ADDR_INET(addr,0))
85
let int_of_file_descr =
86
match Sys.os_type with
87
| "Unix" | "Cygwin" ->
88
(fun fd -> (Obj.magic (fd:file_descr) : int))
90
invalid_arg "Netsys.int_of_file_descr"
92
let file_descr_of_int =
93
match Sys.os_type with
94
| "Unix" | "Cygwin" ->
95
(fun n -> (Obj.magic (n:int) : file_descr))
97
invalid_arg "Netsys.file_descr_of_int"
614
let protostring_of_inet_addr ip = (Obj.magic ip)
616
let inet_addr_of_protostring s =
617
let l = String.length s in
618
if l = 4 || l = 16 then (Obj.magic s) else
619
invalid_arg "Netsys.inet_addr_of_protostring"
100
621
external _exit : int -> unit = "netsys__exit";;
102
(* Limits & resources *)
104
external sysconf_open_max : unit -> int = "netsys_sysconf_open_max";;
106
(* Process groups, sessions, terminals *)
108
external getpgid : int -> int = "netsys_getpgid";;
109
let getpgrp() = getpgid 0;;
110
external setpgid : int -> int -> unit = "netsys_setpgid";;
111
let setpgrp() = setpgid 0 0;;
113
external tcgetpgrp : file_descr -> int = "netsys_tcgetpgrp";;
114
external tcsetpgrp : file_descr -> int -> unit = "netsys_tcsetpgrp";;
116
external ctermid : unit -> string = "netsys_ctermid";;
117
external ttyname : file_descr -> string = "netsys_ttyname";;
119
external getsid : int -> int = "netsys_getsid";;
121
(* Users and groups *)
123
external setreuid : int -> int -> unit = "netsys_setreuid";;
124
external setregid : int -> int -> unit = "netsys_setregid";;
126
(* POSIX shared memory *)
128
external have_posix_shm : unit -> bool = "netsys_have_posix_shm"
622
(* same external also in netsys_signal.ml *)
625
external mcast_set_loop : Unix.file_descr -> bool -> unit
626
= "netsys_mcast_set_loop"
627
external mcast_set_ttl : Unix.file_descr -> int -> unit
628
= "netsys_mcast_set_ttl"
629
external mcast_add_membership :
630
Unix.file_descr -> Unix.inet_addr -> Unix.inet_addr -> unit
631
= "netsys_mcast_add_membership"
632
external mcast_drop_membership :
633
Unix.file_descr -> Unix.inet_addr -> Unix.inet_addr -> unit
634
= "netsys_mcast_drop_membership"
637
let f_moncontrol = ref (fun _ -> ())
642
let set_moncontrol f =
647
(* Compatibility with older ocamlnet versions *)
649
let really_write = really_gwrite `Read_write
650
let blocking_read = blocking_gread `Read_write
651
let really_read = really_gread `Read_write
653
let int_of_file_descr = Netsys_posix.int_of_file_descr
654
let file_descr_of_int = Netsys_posix.file_descr_of_int
656
let have_posix_shm = Netsys_posix.have_posix_shm
659
Netsys_posix.shm_open_flag =
135
external shm_open : string -> shm_open_flag list -> int -> file_descr
137
external shm_unlink : string -> unit = "netsys_shm_unlink"
666
let shm_open = Netsys_posix.shm_open
667
let shm_unlink = Netsys_posix.shm_unlink