File: netsys_posix.ml

package info (click to toggle)
ocamlnet 4.1.2-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 52,032 kB
  • sloc: ml: 148,435; ansic: 11,014; sh: 1,898; makefile: 1,355
file content (1410 lines) | stat: -rw-r--r-- 37,010 bytes parent folder | download | duplicates (5)
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
(* $Id$ *)

open Unix
open Printf


module Debug = struct
  let enable = ref false
end

let dlog = Netlog.Debug.mk_dlog "Netsys_posix" Debug.enable
let dlogr = Netlog.Debug.mk_dlogr "Netsys_posix" Debug.enable

let () =
  Netlog.Debug.register_module "Netsys_posix" Debug.enable

external int64_of_file_descr : Unix.file_descr -> int64
  = "netsys_int64_of_file_descr"

let int_of_file_descr =
  match Sys.os_type with
    | "Unix" | "Cygwin" ->
	(fun fd -> (Obj.magic (fd:file_descr) : int))
    | _ ->
	(fun fd -> invalid_arg "Netsys.int_of_file_descr")

let file_descr_of_int =
  match Sys.os_type with
    | "Unix" | "Cygwin" ->
	(fun n -> (Obj.magic (n:int) : file_descr))
    | _ ->
	(fun n -> invalid_arg "Netsys.file_descr_of_int")

(* Limits  & resources *)

external sysconf_open_max : unit -> int = "netsys_sysconf_open_max";;

(* misc *)

external fchdir : Unix.file_descr -> unit = "netsys_fchdir" ;;
external fdopendir : Unix.file_descr -> Unix.dir_handle = "netsys_fdopendir" ;;
external realpath : string -> string = "netsys_realpath"
external get_nonblock : Unix.file_descr -> bool = "netsys_get_nonblock"

(* Process groups, sessions, terminals *)

external getpgid : int -> int = "netsys_getpgid";;
let getpgrp() = getpgid 0;;
external setpgid : int -> int -> unit = "netsys_setpgid";;
let setpgrp() = setpgid 0 0;;

external tcgetpgrp : file_descr -> int = "netsys_tcgetpgrp";;
external tcsetpgrp : file_descr -> int -> unit = "netsys_tcsetpgrp";;

external ctermid : unit -> string = "netsys_ctermid";;
external ttyname : file_descr -> string = "netsys_ttyname";;

external getsid : int -> int = "netsys_getsid";;

external posix_openpt : bool -> Unix.file_descr = "netsys_posix_openpt"
external grantpt : Unix.file_descr -> unit = "netsys_grantpt"
external unlockpt : Unix.file_descr -> unit = "netsys_unlockpt"
external ptsname : Unix.file_descr -> string = "netsys_ptsname"


let with_tty f =
  let fd = 
    try Unix.openfile (ctermid()) [Unix.O_RDWR] 0 
    with _ -> failwith "Netsys_posix.with_tty: cannot open terminal" in
  try
    let r = f fd in
    Unix.close fd;
    r
  with error -> Unix.close fd; raise error


let descr_input_line fd = (* unbuffered! *)
  let b = Buffer.create 80 in
  let s = Bytes.create 1 in

  let rec loop () =
    try
      let n = Unix.read fd s 0 1 in
      if n > 0 && Bytes.get s 0 <> '\n' then (
        #ifdef HAVE_BYTES
	  Buffer.add_bytes b s;
        #else
          Buffer.add_string b (Bytes.unsafe_to_string s);
        #endif
	raise(Unix.Unix_error(Unix.EINTR,"",""))
      )
    with
      | Unix.Unix_error(Unix.EINTR,_,_) ->
	  loop()
  in

  loop();
  Buffer.contents b



let tty_read_password ?(tty=Unix.stdin) prompt =
  if Unix.isatty tty then (
    let cleanup = ref [] in
    let f = Unix.out_channel_of_descr tty in
    output_string f prompt;
    flush f;
    Unix.tcdrain tty;
    Unix.tcflush tty Unix.TCIFLUSH;
    let p = Unix.tcgetattr tty in
    try
      let p' =
	{ p with
	    Unix.c_echo = false;
	    Unix.c_echoe = false;
	    Unix.c_echok = false;
	    Unix.c_echonl = false
	} in
      Unix.tcsetattr tty Unix.TCSAFLUSH p';
      cleanup := (fun () -> Unix.tcsetattr tty Unix.TCSAFLUSH p) :: !cleanup;
      let old_sigint = 
	Sys.signal Sys.sigint (Sys.Signal_handle(fun _ -> raise Sys.Break)) in
      cleanup := (fun () -> Sys.set_signal Sys.sigint old_sigint) :: !cleanup;
      let pw = descr_input_line tty in
      output_string f "\n";
      flush f;
      List.iter (fun f -> f()) !cleanup;
      pw
    with
      | error ->
	  List.iter (fun f -> f()) !cleanup;
	  if error = Sys.Break then (
	    output_string f "\n";
	    flush f
	  );
	  raise error
  )
  else
    descr_input_line tty


(* Users and groups *)

external setreuid : int -> int -> unit = "netsys_setreuid";;
external setregid : int -> int -> unit = "netsys_setregid";;
external initgroups : string -> int -> unit = "netsys_initgroups"

(* mknod *)

type node_type = 
  | S_IFREG 
  | S_IFCHR of int  (* major + minor *)
  | S_IFBLK of int  (* major + minor *)
  | S_IFIFO
  | S_IFSOCK
      
external mknod : string -> int -> node_type -> unit = "netsys_mknod"

(* poll *)

external pollfd_size : unit -> int = "netsys_pollfd_size"

let the_pollfd_size = pollfd_size()

let _have_poll = the_pollfd_size > 0
let have_poll() = _have_poll

type poll_req_events = int
type poll_act_events = int
type poll_cell =
    { mutable poll_fd : Unix.file_descr;
      mutable poll_req_events : poll_req_events;
      mutable poll_act_events : poll_act_events;
    }
type poll_mem
type poll_array =
  | Poll_mem of poll_mem * int (*length*)
  | Poll_emu of poll_cell array

let null_poll_cell =
  { poll_fd = Unix.stdin;
    poll_req_events = 0;
    poll_act_events = 0
  }

external mk_poll_mem : int -> poll_mem
  = "netsys_mk_poll_mem"

external set_poll_mem : poll_mem -> int -> Unix.file_descr -> int -> int -> unit
  = "netsys_set_poll_mem"

external get_poll_mem : poll_mem -> int -> (Unix.file_descr * int * int)
  = "netsys_get_poll_mem"

external blit_poll_mem : poll_mem -> int -> poll_mem -> int -> int -> unit
  = "netsys_blit_poll_mem"

external poll_constants : unit -> int array = "netsys_poll_constants"

let the_poll_constants = poll_constants()

let const_rd_event = the_poll_constants.(0)
let const_pri_event = the_poll_constants.(1)
let const_wr_event = the_poll_constants.(2)
let const_err_event = the_poll_constants.(3)
let const_hup_event = the_poll_constants.(4)
let const_nval_event = the_poll_constants.(5)

let poll_req_events rd wr pri =
  (if rd then const_rd_event else 0) lor
  (if wr then const_wr_event else 0) lor
  (if pri then const_pri_event else 0)

let poll_req_triple p =
  (p land const_rd_event <> 0,
   p land const_wr_event <> 0,
   p land const_pri_event <> 0
  )

let poll_null_events() = 0

let poll_result p = p <> 0
let poll_rd_result p = p land const_rd_event <> 0
let poll_wr_result p = p land const_wr_event <> 0
let poll_pri_result p = p land const_pri_event <> 0
let poll_err_result p = p land const_err_event <> 0
let poll_hup_result p = p land const_hup_event <> 0
let poll_nval_result p = p land const_nval_event <> 0

let poll_array_length =
  function
    | Poll_mem(_,n) -> n
    | Poll_emu e -> Array.length e

let set_poll_cell a k c =
  if k < 0 || k >= poll_array_length a then
    invalid_arg "Netsys.set_poll_cell";
  match a with
    | Poll_mem(s,_) ->
	set_poll_mem s k c.poll_fd c.poll_req_events (* c.poll_revents *) 0 
    | Poll_emu e ->
	e.(k) <- { c with poll_fd = c.poll_fd } (* copy *)

let get_poll_cell a k =
  if k < 0 || k >= poll_array_length a then
    invalid_arg "Netsys.get_poll_cell";
  match a with
    | Poll_mem(s,_) ->
	let (fd, ev, rev) = get_poll_mem s k in
	{ poll_fd = fd; poll_req_events = ev; poll_act_events = rev }
    | Poll_emu e ->
	let c = e.(k) in
	{ c with poll_fd = c.poll_fd }   (* copy *)

let blit_poll_array a1 k1 a2 k2 len =
  let l1 = poll_array_length a1 in
  let l2 = poll_array_length a2 in
  if len < 0 || k1 < 0 || k1+len > l1 || k2 < 0 || k2+len > l2 then
    invalid_arg "Netsys.get_poll_cell";
  match (a1, a2) with
    | (Poll_mem(s1,_), Poll_mem(s2,_)) ->
	blit_poll_mem s1 k1 s2 k2 len
    | (Poll_emu e1, Poll_emu e2) ->
	Array.blit e1 k1 e2 k2 len
    | _ ->
	assert false

let create_poll_array n =
  if _have_poll then (
    let s = mk_poll_mem n in
    Poll_mem(s,n)
  )
  else (
    let e = Array.make n null_poll_cell in
    Poll_emu e
  )

external netsys_poll : poll_mem -> int -> int -> int = "netsys_poll"


let concat_fd_list l =
  String.concat "," 
    (List.map
       (fun fd -> 
	  Int64.to_string(int64_of_file_descr fd))
       l
    )


(* win32 only: *)
external netsys_real_select : 
         Unix.file_descr list -> 
         Unix.file_descr list -> 
         Unix.file_descr list -> 
         float ->
           (Unix.file_descr list * Unix.file_descr list * Unix.file_descr list)
  = "netsys_real_select"

let real_select = 
  if Sys.os_type = "Win32" then
    netsys_real_select
  else
    Unix.select


let do_poll a k tmo =
  match a with
    | Poll_mem(s,_) ->
	netsys_poll s k tmo
    | Poll_emu e ->
	(* Emulate poll using Unix.select. This is slow! *)
	let tmo' =
	  if tmo < 0 then (-1.0) else float tmo *. 0.001 in
	let l_inp = ref [] in
	let l_out = ref [] in
	let l_pri = ref [] in
	for j = 0 to k-1 do
	  let c = e.(j) in
	  let (f_inp, f_out, f_pri) = poll_req_triple c.poll_req_events in
	  if f_inp then l_inp := c.poll_fd :: !l_inp;
	  if f_out then l_out := c.poll_fd :: !l_out;
	  if f_pri then l_pri := c.poll_fd :: !l_pri;
	done;
	dlogr (fun () ->
		 sprintf "poll_emulation request in=%s out=%s pri=%s tmo=%f"
		   (concat_fd_list !l_inp)
		   (concat_fd_list !l_out)
		   (concat_fd_list !l_pri)
		   tmo');
	let (o_inp, o_out, o_pri) = 
	  real_select !l_inp !l_out !l_pri tmo' in
	dlogr (fun () ->
		 sprintf "poll_emulation result in=%s out=%s pri=%s"
		   (concat_fd_list o_inp)
		   (concat_fd_list o_out)
		   (concat_fd_list o_pri));
	let a_inp = Array.of_list o_inp in
	let a_out = Array.of_list o_out in
	let a_pri = Array.of_list o_pri in
	Array.sort Pervasives.compare a_inp;
	Array.sort Pervasives.compare a_out;
	Array.sort Pervasives.compare a_pri;
	let n = ref 0 in
	for j = 0 to k-1 do
	  let c = e.(j) in
	  let g_inp = Netsys_impl_util.mem_sorted_array c.poll_fd a_inp in
	  let g_out = Netsys_impl_util.mem_sorted_array c.poll_fd a_out in
	  let g_pri = Netsys_impl_util.mem_sorted_array c.poll_fd a_pri in
	  let rev =
	    (if g_inp then const_rd_event else 0) lor
	    (if g_out then const_wr_event else 0) lor
	    (if g_pri then const_pri_event else 0) in
	  c.poll_act_events <- rev;
	  if rev <> 0 then incr n
	done;
	!n


let poll a k tmo =
  if k < 0 || k > poll_array_length a then
    invalid_arg "Netsys.poll";
  let r =
    Netsys_impl_util.slice_time_ms
      (fun tmo0 ->
	 (* tmo0 is now an int in milliseconds *)
	 let n = do_poll a k tmo0 in
	 if n = 0 then None else Some n
      )
      tmo in
  match r with
    | None -> 0
    | Some n -> n


let restarting_poll a k tmo =
  Netsys_impl_util.restart_tmo (poll a k) tmo

let poll_single fd r w pri tmo =
  let a = create_poll_array 1 in
  set_poll_cell a 0 { poll_fd = fd; 
		      poll_req_events = poll_req_events r w pri;
		      poll_act_events = poll_null_events()
		    };
  poll a 1 tmo > 0


let act_events_of_int n = n
let int_of_act_events n = n

let req_events_of_int n = n
let int_of_req_events n = n

(* poll aggregation *)

type netsys_event_source
type netsys_event_aggreg

type tagged_event =
  | EV_FD of Unix.file_descr

type event_push =
    { event_id : int;
      event_tag : tagged_event;
      event_api_req : int;
    }

class type event_source =
object
  method event_id : int
  method event_tag : tagged_event
  method push_record : event_push
  method need_push : bool
  method post_poll : int -> int -> unit
  method upd_kernel_events : int -> unit
  method set_post_modify_callback : (int -> unit) -> unit
  method clear_post_modify_callback : unit -> unit
  method modify_fd_event_source : int -> unit
  method act_events : int
end

class fd_event_source fd req : event_source =
  let event_id = ref 0 in
  let event_api_req = ref req in
  let event_kernel_req = ref 0 in
  let event_tag = EV_FD fd in
  let act_events = ref 0 in
  let post_modify_callback = ref None in
object(self)
  initializer (
    let id = Oo.id self in
    event_id := id
  )
    
  method event_id = !event_id
  method event_tag = event_tag
  method push_record =
    { event_id = !event_id;
      event_tag = event_tag;
      event_api_req= !event_api_req
    }
  method need_push = !event_api_req <> !event_kernel_req
  method upd_kernel_events req =
    event_kernel_req := req;
  method post_poll mask act = 
    event_kernel_req := !event_kernel_req land mask;
    act_events := act
  method set_post_modify_callback f =
    match !post_modify_callback with
      | None -> post_modify_callback := Some f
      | Some _ ->
	  failwith "Netsys_posix.add_event_source: the source is already \
                    added to an aggregator"
  method clear_post_modify_callback () =
    post_modify_callback := None
  method modify_fd_event_source req =
    event_api_req := req;
    match !post_modify_callback with
      | Some f -> f !event_id
      | None -> ()
  method act_events = !act_events
end

let fd_event_source fd req = 
  new fd_event_source fd req

let modify_fd_event_source es req =
  es # modify_fd_event_source req

let get_fd_of_event_source es =
  match es # event_tag with
    | EV_FD fd -> fd

let act_events_of_event_source es =
  es # act_events


external have_event_aggregation : unit -> bool
  = "netsys_have_event_aggregation"

external netsys_create_event_aggreg :
  bool -> netsys_event_aggreg
  = "netsys_create_event_aggreg"

external netsys_destroy_event_aggreg :
  netsys_event_aggreg -> unit
  = "netsys_destroy_event_aggreg"

external netsys_event_aggreg_fd :
  netsys_event_aggreg -> Unix.file_descr
  = "netsys_event_aggreg_fd"

external netsys_push_event_sources : 
  netsys_event_aggreg -> event_push list -> unit
  = "netsys_push_event_sources"

external netsys_add_event_source :
  netsys_event_aggreg -> event_push -> unit
  = "netsys_add_event_source"

external netsys_del_event_source :
  netsys_event_aggreg -> int -> tagged_event -> unit
  = "netsys_del_event_source"

external netsys_poll_event_sources :
  netsys_event_aggreg -> int -> (int * int * int) list
  (* The list are triples (event_id, mask, act) *)
  = "netsys_poll_event_sources"

external netsys_interrupt_aggreg :
  netsys_event_aggreg -> unit
  = "netsys_interrupt_aggreg"

class event_aggregator is_interruptible =
  let up = ref true in
  let all_sources = Hashtbl.create 27 in
  let upd_sources = Hashtbl.create 27 in
  let netsys = netsys_create_event_aggreg is_interruptible in
  let check_up() =
    if not !up then
      failwith "Netsys_posix: This event_aggregator is already destroyed" in
object(self)
  method add_event_source (es : event_source) =
    check_up();
    es # set_post_modify_callback self#post_modify_callback;
    try
      netsys_add_event_source netsys es#push_record;
      Hashtbl.replace all_sources es#event_id es;
      Hashtbl.replace upd_sources es#event_id es
    with
      | error ->
	  es # clear_post_modify_callback();
	  raise error
  method del_event_source (es : event_source) =
    check_up();
    if not(Hashtbl.mem all_sources es#event_id) then
      failwith "Netsys_posix.del_event_source: not member of this aggregator";
    netsys_del_event_source netsys es#event_id es#event_tag;
    es # clear_post_modify_callback();
    Hashtbl.remove all_sources es#event_id;
    Hashtbl.remove upd_sources es#event_id
  method post_modify_callback id =
    check_up();
    if (Hashtbl.mem all_sources id) then (
      let es = Hashtbl.find all_sources id in
      Hashtbl.replace upd_sources id es
    )
  method push_event_updates() =
    check_up();
    let l =
      Hashtbl.fold 
	(fun _ es acc -> 
	   if es # need_push then
	     let p = es#push_record in
	     es # upd_kernel_events p.event_api_req;
	     p :: acc
	   else
	     acc
	) 
	upd_sources
	[] in
    netsys_push_event_sources netsys l;
    Hashtbl.clear upd_sources
  method poll_event_sources tmo =
    self # push_event_updates();
    let v =
      Netsys_impl_util.slice_time_ms
	(fun millis ->
	   let evpairs0 = netsys_poll_event_sources netsys millis in
	   let evpairs1 = (* without spurious events *)
	     List.filter
	       (fun (id,_,_) ->
		  Hashtbl.mem all_sources id
	       )
	       evpairs0 in
	   let evpairs2 =
	     List.map
	       (fun (id,mask,act) ->
		  let es = Hashtbl.find all_sources id in
		  es#post_poll mask act;
		  Hashtbl.replace upd_sources id es;
		  es
	       )
	       evpairs1 in
	   if evpairs2 = [] then
	     None
	   else
	     Some evpairs2
	)
	tmo in
    match v with
      | None -> []
      | Some l -> l
  method event_aggregator_fd =
    check_up();
    netsys_event_aggreg_fd netsys
  method interrupt_event_aggregator() =
    netsys_interrupt_aggreg netsys
  method destroy_event_aggregator () =
    if !up then (
      netsys_destroy_event_aggreg netsys;
      up := false
    )
end

let create_event_aggregator = new event_aggregator
let add_event_source ea = ea # add_event_source
let del_event_source ea = ea # del_event_source
let push_event_updates ea = ea # push_event_updates()
let poll_event_sources (ea : event_aggregator) = ea # poll_event_sources
let event_aggregator_fd ea = ea # event_aggregator_fd
let destroy_event_aggregator ea = ea # destroy_event_aggregator()
let interrupt_event_aggregator ea = ea # interrupt_event_aggregator()


(* events *)

type not_event

external nsys_create_event : bool -> not_event = "netsys_create_not_event"
external set_nonblock_event : not_event -> unit = "netsys_set_nonblock_not_event"
external get_event_fd_nodup : not_event -> Unix.file_descr = "netsys_get_not_event_fd_nodup"
external set_event : not_event -> unit = "netsys_set_not_event"
external wait_event : not_event -> unit = "netsys_wait_not_event"
external consume_event : not_event -> unit = "netsys_consume_not_event"
external nsys_destroy_event : not_event -> unit = "netsys_destroy_not_event"
external nsys_return_all_event_fd : not_event -> Unix.file_descr list
  = "netsys_return_all_not_event_fd"

let create_event() = 
  let e = nsys_create_event true in
  List.iter
    (fun fd ->
       Netlog.Debug.track_fd
	 ~owner:"Netsys_posix" 
	 ~descr:"create_event"
	 fd;
    )
    (nsys_return_all_event_fd e);
  e

let destroy_event e =
  List.iter
    (fun fd ->
       Netlog.Debug.release_fd fd;
    )
    (nsys_return_all_event_fd e);
  nsys_destroy_event e


let get_event_fd e =
  let fd = get_event_fd_nodup e in
  let fd = Unix.dup fd in
  Unix.set_close_on_exec fd;
  fd


let report_signal_as_event ne signum =
  (* This is simpler to implement in Ocaml than in C:
     - [ne] is kept reachable all the time
     - we can pass [ne] to the handler via a closure
     - we can run non-async-signal-safe stuff in Ocaml signal handlers,
       because they are actually called indirectly
   *)
  Sys.set_signal 
    signum
    (Sys.Signal_handle
       (fun _ ->
	  set_event ne
       )
    )

(* post fork handlers *)

class type post_fork_handler =
object
  method name : string
  method run : unit -> unit
end

module PFH = struct
  type t = post_fork_handler
  let compare = Pervasives.compare
end

module PFH_Set = Set.Make(PFH)

let post_fork_registry = ref PFH_Set.empty
let post_fork_mutex = !Netsys_oothr.provider # create_mutex()

let register_post_fork_handler pfh =
  post_fork_mutex # lock();
  post_fork_registry := PFH_Set.add pfh !post_fork_registry;
  post_fork_mutex # unlock()

let remove_post_fork_handler pfh =
  post_fork_mutex # lock();
  post_fork_registry := PFH_Set.remove pfh !post_fork_registry;
  post_fork_mutex # unlock()

let run_post_fork_handlers() =
  PFH_Set.iter
    (fun pfh ->
       try pfh#run()
       with
	 | error ->
	     prerr_endline("Netsys_posix: Exception in post fork handler "
			   ^ pfh#name ^ ": " ^ Netexn.to_string error)
    )
    !post_fork_registry


(* "at" *)

type at_flag = AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_SYMLINK_FOLLOW | 
               AT_REMOVEDIR

(* The stubs assume these type definitions: *)
#ifdef HAVE_O_KEEPEXEC
type open_flag1 = Unix.open_flag =
    O_RDONLY | O_WRONLY | O_RDWR | O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC
  | O_EXCL | O_NOCTTY | O_DSYNC | O_SYNC | O_RSYNC | O_SHARE_DELETE
  | O_CLOEXEC | O_KEEPEXEC
#else
#ifdef HAVE_O_CLOEXEC
type open_flag1 = Unix.open_flag =
    O_RDONLY | O_WRONLY | O_RDWR | O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC
  | O_EXCL | O_NOCTTY | O_DSYNC | O_SYNC | O_RSYNC | O_SHARE_DELETE
  | O_CLOEXEC
#else
#ifdef HAVE_O_SHARE_DELETE
type open_flag1 = Unix.open_flag =
    O_RDONLY | O_WRONLY | O_RDWR | O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC
  | O_EXCL | O_NOCTTY | O_DSYNC | O_SYNC | O_RSYNC | O_SHARE_DELETE
#else
type open_flag1 = Unix.open_flag =
    O_RDONLY | O_WRONLY | O_RDWR | O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC
  | O_EXCL | O_NOCTTY | O_DSYNC | O_SYNC | O_RSYNC
#endif
#endif
#endif

type access_permission1 = Unix.access_permission =
    R_OK | W_OK | X_OK | F_OK


external netsys_at_fdcwd : unit -> Unix.file_descr =
    "netsys_at_fdcwd"

let at_fdcwd = netsys_at_fdcwd()

external have_at : unit -> bool 
  = "netsys_have_at"
external openat :  Unix.file_descr -> string -> Unix.open_flag list -> 
                   Unix.file_perm ->  Unix.file_descr
  = "netsys_openat"
external faccessat : Unix.file_descr -> string -> Unix.access_permission list ->
                     at_flag list -> unit
  = "netsys_faccessat"
external mkdirat : Unix.file_descr -> string -> int -> unit 
  = "netsys_mkdirat"
external renameat : Unix.file_descr -> string -> Unix.file_descr -> string -> 
                    unit
  = "netsys_renameat"
external linkat : Unix.file_descr -> string -> Unix.file_descr -> string ->
                  at_flag list -> unit
  = "netsys_linkat"
external unlinkat : Unix.file_descr -> string -> at_flag list -> unit
  = "netsys_unlinkat"
external symlinkat : string -> Unix.file_descr -> string -> unit
  = "netsys_symlinkat"
external mkfifoat : Unix.file_descr -> string -> int -> unit
  = "netsys_mkfifoat"
external readlinkat : Unix.file_descr -> string -> string
  = "netsys_readlinkat"

(* Clocks *)

type timespec = float * int
type clock_id
type clock =
  (* also in Netlog *)
  | CLOCK_REALTIME
  | CLOCK_MONOTONIC
  | CLOCK_ID of clock_id

external nanosleep : timespec -> timespec ref -> unit = "netsys_nanosleep"
external clock_gettime : clock -> timespec = "netsys_clock_gettime"
         (* also in Netlog *)
external clock_settime : clock -> timespec -> unit = "netsys_clock_settime"
external clock_getres : clock -> timespec = "netsys_clock_getres"
external clock_getcpuclockid : int -> clock_id = "netsys_clock_getcpuclockid"

type timer_expiration =
  | TEXP_NONE
  | TEXP_EVENT of not_event
  | TEXP_EVENT_CREATE
  | TEXP_SIGNAL of int
type posix_timer

external have_posix_timer : unit -> bool
  = "netsys_have_posix_timer"

external nsys_timer_create : clock -> timer_expiration -> posix_timer
  = "netsys_timer_create"

external timer_settime : posix_timer -> bool -> timespec -> timespec -> unit
  = "netsys_timer_settime"

external timer_gettime : posix_timer -> timespec
  = "netsys_timer_gettime"

external timer_delete : posix_timer -> unit
  = "netsys_timer_delete"

external timer_event : posix_timer -> not_event
  = "netsys_timer_event"

let timer_create clock texp =
  let v = nsys_timer_create clock texp in
  Gc.finalise timer_delete v;
  v

(* Spawn *)

type wd_spec =
  | Wd_keep
  | Wd_chdir of string
  | Wd_fchdir of Unix.file_descr

type pg_spec =
  | Pg_keep
  | Pg_new_bg_group
  | Pg_new_fg_group
  | Pg_join_group of int

type fd_action =
  | Fda_close of Unix.file_descr
  | Fda_close_ignore of Unix.file_descr
  | Fda_close_except of bool array
  | Fda_dup2 of Unix.file_descr * Unix.file_descr

type sig_action =
  | Sig_default of int
  | Sig_ignore of int
  | Sig_mask of int list

external netsys_spawn : wd_spec -> pg_spec -> fd_action list -> 
                        sig_action list -> string array ->
                        string -> string array -> int
  = "netsys_spawn_byte" "netsys_spawn_nat"

external netsys_posix_spawn : pg_spec -> fd_action list -> 
                              sig_action list -> string array ->
                              string -> string array -> int
  = "netsys_posix_spawn_byte" "netsys_posix_spawn_nat"

external have_posix_spawn : unit -> bool
  = "netsys_have_posix_spawn"

let spawn ?(chdir = Wd_keep) ?(pg = Pg_keep) ?(fd_actions = [])
          ?(sig_actions = []) ?(env = Unix.environment()) 
          ?(no_posix_spawn=false) cmd args =
  (* Check whether we can use the faster netsys_posix_spawn *)
  let use_posix_spawn =
    not no_posix_spawn &&
    have_posix_spawn() &&
    chdir = Wd_keep &&
    pg <> Pg_new_fg_group &&
    not (List.exists
	   (fun sa -> match sa with Sig_ignore _ -> true | _ -> false) 
	   sig_actions) in
  
  try
    if not use_posix_spawn then
      failwith "USE_FORK_EXEC";

    netsys_posix_spawn pg fd_actions sig_actions env cmd args
      (* may also fail with "USE_FORK_EXEC" in some cases *)
  with
    | Failure "USE_FORK_EXEC" ->
	(* Fixup: if pg = Pg_new_fg_group, we remove any Sig_default for
	   SIGTTOU from sig_actions. Because of special handling, the effect
	   of Sig_default is enforced by the implementation, but this must be
	   done at [execve] time.
	 *)
	let sig_actions =
	  if pg = Pg_new_fg_group then
	    List.filter 
	      (fun spec -> spec <> Sig_default Sys.sigttou)
	      sig_actions
	  else
	    sig_actions in
	netsys_spawn chdir pg fd_actions sig_actions env cmd args

type watched_subprocess = 
    { atom_idx : int;
      mutable alive : bool;
      mutable allocated : bool;
    }

external netsys_watch_subprocess : int -> int -> bool -> Unix.file_descr * int
  = "netsys_watch_subprocess"

external netsys_ignore_subprocess : int -> unit
  = "netsys_ignore_subprocess"

external netsys_forget_subprocess : int -> unit
  = "netsys_forget_subprocess"

external netsys_get_subprocess_status : int -> Unix.process_status option
  = "netsys_get_subprocess_status"

external install_subprocess_handler : unit -> unit
  = "netsys_install_sigchld_handler"

external subprocess_cleanup_after_fork : unit -> unit
  = "netsys_subprocess_cleanup_after_fork"

external netsys_kill_subprocess : int -> int -> unit
  = "netsys_kill_subprocess"

external netsys_killpg_subprocess : int -> int -> unit
  = "netsys_killpg_subprocess"

external kill_all_subprocesses : int -> bool -> bool -> unit
  = "netsys_kill_all_subprocesses"

external killpg_all_subprocesses : int -> bool -> unit
  = "netsys_killpg_all_subprocesses"

let forget_subprocess ws =
  if ws.allocated then (
    netsys_forget_subprocess ws.atom_idx;
    ws.allocated <- false;
  );
  ws.alive <- false

let watch_subprocess pid pgid kill_flag =
  if pid <= 0 || pgid < 0 then
    invalid_arg "Netsys_posix.watch_subprocess";
  let fd, atom_idx = netsys_watch_subprocess pid pgid kill_flag in
  let ws = { atom_idx = atom_idx; alive = true; allocated = true } in
  Gc.finalise forget_subprocess ws;
  (fd, ws)

let ignore_subprocess ws =
  if not ws.alive then
    failwith "Netsys_posix.ignore_subprocess: stale reference";
  netsys_ignore_subprocess ws.atom_idx;
  ws.alive <- false

let get_subprocess_status ws =
  if not ws.alive then
    failwith "Netsys_posix.get_subprocess_status: stale reference";
  netsys_get_subprocess_status ws.atom_idx

let kill_subprocess signal ws =
  if ws.alive then
    netsys_kill_subprocess signal ws.atom_idx

let killpg_subprocess signal ws =
  if ws.alive then
    netsys_killpg_subprocess signal ws.atom_idx


let () =
  register_post_fork_handler
    ( object 
	method name = "subprocess_cleanup_after_fork"
	method run = subprocess_cleanup_after_fork
      end
    )

let register_subprocess_handler() =
  Netsys_signal.register_exclusive_handler
    ~name:"Sigchld handler in Netsys_posix"
    ~signal:Sys.sigchld
    ~install:install_subprocess_handler
    ()


(* locales *)

type langinfo =
    { nl_CODESET : string;
      nl_D_T_FMT : string;
      nl_D_FMT : string;
      nl_T_FMT : string;
      nl_T_FMT_AMPM : string;
      nl_AM_STR : string;
      nl_PM_STR : string;
      nl_DAY_1 : string;
      nl_DAY_2 : string;
      nl_DAY_3 : string;
      nl_DAY_4 : string;
      nl_DAY_5 : string;
      nl_DAY_6 : string;
      nl_DAY_7 : string;
      nl_ABDAY_1 : string;
      nl_ABDAY_2 : string;  
      nl_ABDAY_3 : string;  
      nl_ABDAY_4 : string;  
      nl_ABDAY_5 : string;  
      nl_ABDAY_6 : string;  
      nl_ABDAY_7 : string;  
      nl_MON_1 : string;  
      nl_MON_2 : string;  
      nl_MON_3 : string;  
      nl_MON_4 : string;  
      nl_MON_5 : string;  
      nl_MON_6 : string;  
      nl_MON_7 : string;  
      nl_MON_8 : string;  
      nl_MON_9 : string;  
      nl_MON_10 : string;  
      nl_MON_11 : string;  
      nl_MON_12 : string;  
      nl_ABMON_1 : string;  
      nl_ABMON_2 : string;  
      nl_ABMON_3 : string;  
      nl_ABMON_4 : string;  
      nl_ABMON_5 : string;  
      nl_ABMON_6 : string;  
      nl_ABMON_7 : string;  
      nl_ABMON_8 : string;  
      nl_ABMON_9 : string;  
      nl_ABMON_10 : string;  
      nl_ABMON_11 : string;  
      nl_ABMON_12 : string;  
      nl_ERA : string;  
      nl_ERA_D_FMT : string;  
      nl_ERA_D_T_FMT : string;  
      nl_ERA_T_FMT : string;  
      nl_ALT_DIGITS : string;  
      nl_RADIXCHAR : string;  
      nl_THOUSEP : string;  
      nl_YESEXPR : string;  
      nl_NOEXPR : string;  
      nl_CRNCYSTR : string;  
    }

external netsys_query_langinfo : string -> langinfo = "netsys_query_langinfo"

let cached_langinfo = ref None

let query_langinfo locale =
  if locale = "" then (
    match !cached_langinfo with
      | None ->
	  let li = netsys_query_langinfo "" in
	  cached_langinfo := Some li;
	  li
      | Some li -> li
  )
  else
    netsys_query_langinfo locale



(* syslog *)

type level = Netlog.level

type m_level =
  | LOG_EMERG | LOG_ALERT | LOG_CRIT | LOG_ERR | LOG_WARNING 
  | LOG_NOTICE | LOG_INFO | LOG_DEBUG

let trans_level =
  [ `Emerg, LOG_EMERG;
    `Alert, LOG_ALERT;
    `Crit, LOG_CRIT;
    `Err, LOG_ERR;
    `Warning, LOG_WARNING;
    `Notice, LOG_NOTICE;
    `Info, LOG_INFO;
    `Debug, LOG_DEBUG
  ]

type syslog_facility =
    [ `Authpriv
    | `Cron
    | `Daemon
    | `Ftp
    | `Kern
    | `Local0
    | `Local1
    | `Local2
    | `Local3
    | `Local4
    | `Local5
    | `Local6
    | `Local7
    | `Lpr
    | `Mail
    | `News
    | `Syslog
    | `User
    | `Uucp
    | `Default
    ]

type m_syslog_facility = 
  | LOG_AUTHPRIV
  | LOG_CRON
  | LOG_DAEMON
  | LOG_FTP
  | LOG_KERN
  | LOG_LOCAL0
  | LOG_LOCAL1
  | LOG_LOCAL2
  | LOG_LOCAL3
  | LOG_LOCAL4
  | LOG_LOCAL5
  | LOG_LOCAL6
  | LOG_LOCAL7
  | LOG_LPR
  | LOG_MAIL
  | LOG_NEWS
  | LOG_SYSLOG
  | LOG_USER
  | LOG_UUCP
  | LOG_DEFAULT

let trans_facility =
  [ `Authpriv, LOG_AUTHPRIV;
    `Cron, LOG_CRON;
    `Daemon, LOG_DAEMON;
    `Ftp, LOG_FTP;
    `Kern, LOG_KERN;
    `Local0, LOG_LOCAL0;
    `Local1, LOG_LOCAL1;
    `Local2, LOG_LOCAL2;
    `Local3, LOG_LOCAL3;
    `Local4, LOG_LOCAL4;
    `Local5, LOG_LOCAL5;
    `Local6, LOG_LOCAL6;
    `Local7, LOG_LOCAL7;
    `Lpr, LOG_LPR;
    `Mail, LOG_MAIL;
    `News, LOG_NEWS;
    `Syslog, LOG_SYSLOG;
    `User, LOG_USER;
    `Uucp, LOG_UUCP;
    `Default, LOG_DEFAULT;
  ]

type syslog_option =
    [ `Cons
    | `Ndelay
    | `Odelay
    | `Nowait
    | `Pid
    ]

type m_syslog_option =
  | LOG_CONS
  | LOG_NDELAY
  | LOG_ODELAY
  | LOG_NOWAIT
  | LOG_PID

let trans_syslog_option =
  [ `Cons, LOG_CONS;
    `Ndelay, LOG_NDELAY;
    `Odelay, LOG_ODELAY;
    `Nowait, LOG_NOWAIT;
    `Pid, LOG_PID;
  ]

external netsys_openlog : 
  string option -> m_syslog_option list -> m_syslog_facility -> unit
  = "netsys_openlog"

external netsys_syslog :
  m_syslog_facility -> m_level -> string -> unit
  = "netsys_syslog"

external netsys_closelog : unit -> unit = "netsys_closelog"

let openlog id_opt opts fac =
  try
    netsys_openlog
      id_opt
      ( List.map
	  (fun p -> List.assoc p trans_syslog_option)
	opts
      )
      ( List.assoc fac trans_facility )
  with
    | Not_found -> assert false
	  
let syslog fac lev msg =
  try
    netsys_syslog
      ( List.assoc fac trans_facility )
      ( List.assoc lev trans_level )
      msg
  with
    | Not_found -> assert false

let closelog = netsys_closelog


(* Sync *)

external fsync : Unix.file_descr -> unit = "netsys_fsync"
external fdatasync : Unix.file_descr -> unit = "netsys_fdatasync"


(* Optional POSIX functions *)

external have_fadvise : unit -> bool = "netsys_have_posix_fadvise"
type advice =
  | POSIX_FADV_NORMAL
  | POSIX_FADV_SEQUENTIAL
  | POSIX_FADV_RANDOM
  | POSIX_FADV_NOREUSE
  | POSIX_FADV_WILLNEED
  | POSIX_FADV_DONTNEED
  | FADV_NORMAL
  | FADV_SEQUENTIAL
  | FADV_RANDOM
  | FADV_NOREUSE
  | FADV_WILLNEED
  | FADV_DONTNEED
external fadvise : Unix.file_descr -> int64 -> int64 -> advice -> unit
                 = "netsys_fadvise"

external have_fallocate : unit -> bool = "netsys_have_posix_fallocate"
external fallocate : Unix.file_descr -> int64 -> int64 -> unit
                   = "netsys_fallocate"

(* POSIX shared memory *)

external have_posix_shm : unit -> bool = "netsys_have_posix_shm"
type shm_open_flag =
  | SHM_O_RDONLY
  | SHM_O_RDWR
  | SHM_O_CREAT
  | SHM_O_EXCL
  | SHM_O_TRUNC
external shm_open : string -> shm_open_flag list -> int -> file_descr
  = "netsys_shm_open"
external shm_unlink : string -> unit = "netsys_shm_unlink"

let shm_create prefix size =
  let pid = Unix.getpid() in
  let t = Unix.gettimeofday() in
  let rec loop n =
    let id = sprintf "%d/%f/%d" pid t n in
    let dg = Digest.to_hex (Digest.string id) in
    let dg8 = String.sub dg 0 8 in
    let name = sprintf "%s_%s" prefix dg8 in
    try
      let fd =
	shm_open
	  name [SHM_O_RDWR; SHM_O_CREAT; SHM_O_EXCL ] 0o600 in
      ( try Unix.fchmod fd 0o600
	with Unix.Unix_error(Unix.EINVAL,_,_) -> ()
	  (* OSX seems to throw EINVAL here *)
      );
      if size > 0 then
        Unix.ftruncate fd size;
      (fd, name)
    with
      | Unix.Unix_error(Unix.EEXIST,_,_) ->
          loop (n+1) in
  loop 0


type sem_kind = [ `Named | `Anonymous ]

type sem_rep

type 'sem_kind semaphore =
    Netsys_types.memory * sem_rep
      (* We keep a reference to the bigarray to prevent that it is
         collected while a semaphore is stored in it
       *)

type named_semaphore = [ `Named ] semaphore
type anon_semaphore = [ `Anonymous ] semaphore

type sem_open_flag =
  | SEM_O_CREAT
  | SEM_O_EXCL

type sem_wait_behavior =
  | SEM_WAIT_BLOCK
  | SEM_WAIT_NONBLOCK

let dummy_mem =
  Bigarray.Array1.create Bigarray.char Bigarray.c_layout 0

external have_anon_posix_semaphores : unit -> bool 
  = "netsys_have_sem_anon"

external have_named_posix_semaphores : unit -> bool 
  = "netsys_have_sem_named"

let have_posix_semaphores() =
  have_anon_posix_semaphores() && have_named_posix_semaphores()

external netsys_sem_size : unit -> int
  = "netsys_sem_size"

external netsys_sem_value_max : unit -> int
  = "netsys_sem_value_max"

let sem_size = netsys_sem_size()

let sem_value_max = netsys_sem_value_max()


external netsys_sem_open :
  string -> sem_open_flag list -> int -> int -> sem_rep
  = "netsys_sem_open"

let sem_open name flags mode init_value =
  if init_value < 0 || init_value > sem_value_max then
    invalid_arg "Netsys_posix.sem_open";
  let sr = netsys_sem_open name flags mode init_value in
  (dummy_mem, sr)

external netsys_sem_close : sem_rep -> unit 
  = "netsys_sem_close"

let sem_close (_,sr) = netsys_sem_close sr

external sem_unlink : string -> unit 
  = "netsys_sem_unlink"

let sem_create prefix initval =
  let pid = Unix.getpid() in
  let t = Unix.gettimeofday() in
  let rec loop n =
    let id = sprintf "%d/%f/%d" pid t n in
    let dg = Digest.to_hex (Digest.string id) in
    let dg8 = String.sub dg 0 8 in
    let name = sprintf "%s_%s" prefix dg8 in
    try
      let sem =
	sem_open
	  name [SEM_O_CREAT; SEM_O_EXCL] 0o600 initval in
      (sem, name)
    with
      | Unix.Unix_error(Unix.EEXIST,_,_) ->
          loop (n+1) in
  loop 0


external netsys_sem_init : 
  Netsys_types.memory -> int -> bool -> int -> sem_rep
  = "netsys_sem_init"

let sem_init mem pos pshared init_value =
  if pos < 0 || pos > Bigarray.Array1.dim mem - sem_size then
    invalid_arg "Netsys_posix.sem_init";
  if init_value < 0 || init_value > sem_value_max then
    invalid_arg "Netsys_posix.sem_init";
  let sr = netsys_sem_init mem pos pshared init_value in
  (mem,sr)

external netsys_as_sem : Netsys_types.memory -> int -> sem_rep
  = "netsys_as_sem"

let as_sem mem pos = 
  if pos < 0 || pos > Bigarray.Array1.dim mem - sem_size then
    invalid_arg "Netsys_posix.as_sem";
  let sr = netsys_as_sem mem pos in
  (mem,sr)

external netsys_sem_destroy : sem_rep -> unit
  = "netsys_sem_destroy"

let sem_destroy (_,sr) = netsys_sem_destroy sr
  
external netsys_sem_getvalue : sem_rep -> int
  = "netsys_sem_getvalue"

let sem_getvalue (_,sr) = netsys_sem_getvalue sr

external netsys_sem_post : sem_rep -> unit
  = "netsys_sem_post"

let sem_post (_,sr) = netsys_sem_post sr

external netsys_sem_wait : sem_rep -> sem_wait_behavior -> unit
  = "netsys_sem_wait"

let sem_wait (_,sr) b = netsys_sem_wait sr b


type ioprio_target =
  | Ioprio_process of int
  | Ioprio_pgrp of int
  | Ioprio_user of int

type ioprio =
  | Noprio
  | Real_time of int
  | Best_effort of int
  | Idle

external ioprio_get : ioprio_target -> ioprio = "netsys_ioprio_get"
external ioprio_set : ioprio_target -> ioprio -> unit = "netsys_ioprio_set"

let have_ioprio() =
  try let _ = ioprio_get(Ioprio_process(Unix.getpid())) in true
  with _ -> false