io.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /*
  2. * Part of Scheme 48 1.9. See file COPYING for notices and license.
  3. *
  4. * Authors: Richard Kelsey, Jonathan Rees, Marcus Crestani, Ivan Shmakov,
  5. * Mike Sperber
  6. */
  7. /*
  8. * Scheme 48/POSIX I/O interface
  9. */
  10. #include <stdio.h>
  11. #include <sys/types.h>
  12. #include <unistd.h>
  13. #include <fcntl.h>
  14. #include <errno.h>
  15. #include "scheme48.h"
  16. #include "scheme48vm.h" /* ps_close_fd() */
  17. #include "posix.h"
  18. #include "c-mods.h"
  19. #include "unix.h"
  20. #include "fd-io.h"
  21. extern void s48_init_posix_io(void);
  22. static s48_ref_t posix_dup(s48_call_t call, s48_ref_t channel, s48_ref_t new_mode),
  23. posix_dup2(s48_call_t call, s48_ref_t channel, s48_ref_t new_fd),
  24. posix_pipe(s48_call_t call),
  25. posix_close_on_exec_p(s48_call_t call, s48_ref_t channel),
  26. posix_set_close_on_exec(s48_call_t call, s48_ref_t channel,
  27. s48_ref_t close_p),
  28. posix_io_flags(s48_call_t call, s48_ref_t channel, s48_ref_t options);
  29. static s48_ref_t s48_enter_file_options(s48_call_t call, int options);
  30. /*
  31. * Record types imported from Scheme.
  32. */
  33. static s48_ref_t posix_file_options_enum_set_type_binding;
  34. /*
  35. * Install all exported functions in Scheme48.
  36. */
  37. void
  38. s48_init_posix_io(void)
  39. {
  40. S48_EXPORT_FUNCTION(posix_dup);
  41. S48_EXPORT_FUNCTION(posix_dup2);
  42. S48_EXPORT_FUNCTION(posix_pipe);
  43. S48_EXPORT_FUNCTION(posix_close_on_exec_p);
  44. S48_EXPORT_FUNCTION(posix_set_close_on_exec);
  45. S48_EXPORT_FUNCTION(posix_io_flags);
  46. posix_file_options_enum_set_type_binding =
  47. s48_get_imported_binding_2("posix-file-options-enum-set-type");
  48. }
  49. /*
  50. * Moves `channel' to a new file descriptor and returns a new channel that uses
  51. * `channel''s old file descriptor.
  52. *
  53. * Without all the error checking, this is:
  54. * old_fd = channel_os_index(channel);
  55. * new_fd = dup(old_fd);
  56. * s48_set_channel_os_index(channel, new_fd);
  57. * return s48_add_channel(old_fd);
  58. *
  59. */
  60. static s48_ref_t
  61. posix_dup(s48_call_t call, s48_ref_t channel, s48_ref_t new_mode)
  62. {
  63. int new_fd, old_fd, flags;
  64. long status;
  65. s48_ref_t s48_status;
  66. s48_ref_t old_mode;
  67. s48_ref_t new_channel;
  68. if (!s48_channel_p_2(call, channel) ||
  69. s48_eq_p_2(call, s48_channel_status_2(call, channel), s48_channel_status_closed_2(call)))
  70. s48_assertion_violation_2(call, "posix_dup", "not an open channel", 1, channel);
  71. old_fd = s48_unsafe_extract_long_2(call, s48_unsafe_channel_os_index_2(call, channel));
  72. old_mode = s48_unsafe_channel_status_2(call, channel);
  73. RETRY_OR_RAISE_NEG(new_fd, dup(old_fd));
  74. s48_status = s48_set_channel_os_index_2(call, channel, new_fd);
  75. if (!s48_true_p_2(call, s48_status)) {
  76. ps_close_fd(new_fd); /* retries if interrupted */
  77. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, s48_status), 1, channel); }
  78. if (s48_eq_p_2(call, new_mode, s48_channel_status_output_2(call))
  79. && s48_eq_p_2(call, old_mode, s48_channel_status_input_2(call))) {
  80. RETRY_OR_RAISE_NEG(flags, fcntl(new_fd, F_GETFL));
  81. RETRY_OR_RAISE_NEG(status, fcntl(new_fd, F_SETFL, flags | O_NONBLOCK)); }
  82. new_channel = s48_add_channel_2(call,
  83. s48_false_p_2(call, new_mode) ? old_mode : new_mode,
  84. s48_unsafe_channel_id_2(call, channel),
  85. old_fd);
  86. if (!s48_channel_p_2(call, new_channel)) {
  87. ps_close_fd(old_fd); /* retries if interrupted */
  88. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, new_channel), 1, channel); }
  89. return new_channel;
  90. }
  91. /*
  92. * Same again, except that we get told what the new file descriptor is to be.
  93. * We close the channel currently using that descriptor, if there be one.
  94. *
  95. * Without all the error checking, this is:
  96. * old_fd = channel_os_index(channel);
  97. * dup2(old_fd, new_fd);
  98. * s48_set_channel_os_index(channel, new_fd);
  99. * return s48_add_channel(old_fd);
  100. */
  101. static s48_ref_t
  102. posix_dup2(s48_call_t call, s48_ref_t channel, s48_ref_t new_fd)
  103. {
  104. s48_ref_t new_channel;
  105. s48_ref_t s48_status;
  106. int status;
  107. int new_c_fd, old_c_fd;
  108. if (!s48_channel_p_2(call, channel) ||
  109. s48_eq_p_2(call, s48_channel_status_2(call, channel), s48_channel_status_closed_2(call)))
  110. s48_assertion_violation_2(call, "posix_dup2", "not an open channel", 1, channel);
  111. if (!s48_fixnum_p_2(call, new_fd) || new_fd < 0)
  112. s48_assertion_violation_2(call, "posix_dup2", "fd not a nonnegative fixnum", 1, new_fd);
  113. old_c_fd = s48_extract_long_2(call, s48_unsafe_channel_os_index_2(call, channel));
  114. new_c_fd = s48_extract_long_2(call, new_fd);
  115. s48_close_channel(new_c_fd);
  116. RETRY_OR_RAISE_NEG(status, dup2(old_c_fd, new_c_fd));
  117. s48_status = s48_set_channel_os_index_2(call, channel, new_c_fd);
  118. if (!s48_true_p_2(call, s48_status)) {
  119. ps_close_fd(new_c_fd); /* retries if interrupted */
  120. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, s48_status), 1, channel); }
  121. new_channel = s48_add_channel_2(call,
  122. s48_unsafe_channel_status_2(call, channel),
  123. s48_unsafe_channel_id_2(call, channel),
  124. old_c_fd);
  125. if (!s48_channel_p_2(call, new_channel)) {
  126. ps_close_fd(old_c_fd); /* retries if interrupted */
  127. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, new_channel), 1, channel); }
  128. return new_channel;
  129. }
  130. /*
  131. * Opens a pipe and returns a pair (<input-channel> . <output-channel>).
  132. *
  133. * Synopsis:
  134. * int fds[2];
  135. * pipe(fds);
  136. * return s48_cons(s48_add_channel(fds[1]), s48_add_channel(fds[2]));
  137. */
  138. static s48_ref_t
  139. posix_pipe(s48_call_t call)
  140. { int fildes[2],
  141. status;
  142. s48_ref_t in_channel, out_channel;
  143. s48_ref_t id = s48_enter_string_latin_1_2 (call, "pipe");
  144. RETRY_OR_RAISE_NEG(status, pipe(fildes));
  145. in_channel = s48_add_channel_2(call, s48_channel_status_input_2(call), id, fildes[0]);
  146. if (!s48_channel_p_2(call, in_channel)) {
  147. ps_close_fd(fildes[0]); /* retries if interrupted */
  148. ps_close_fd(fildes[1]); /* retries if interrupted */
  149. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, in_channel), 0); }
  150. RETRY_OR_RAISE_NEG(status, fcntl(fildes[1], F_SETFL, O_NONBLOCK));
  151. out_channel = s48_add_channel_2(call, s48_channel_status_output_2(call), id, fildes[1]);
  152. if (!s48_channel_p_2(call, out_channel)) {
  153. s48_close_channel(fildes[0]);
  154. ps_close_fd(fildes[1]); /* retries if interrupted */
  155. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, in_channel), 0); }
  156. return s48_cons_2(call, in_channel, out_channel);
  157. }
  158. static s48_ref_t
  159. posix_close_on_exec_p(s48_call_t call, s48_ref_t channel)
  160. {
  161. int c_fd,
  162. status;
  163. if (!s48_channel_p_2(call, channel) ||
  164. s48_eq_p_2(call,
  165. s48_channel_status_2(call, channel),
  166. s48_channel_status_closed_2(call)))
  167. s48_assertion_violation_2(call, "posix_close_on_exec_p", "not an open channel", 1, channel);
  168. c_fd = s48_unsafe_extract_long_2(call, s48_unsafe_channel_os_index_2(call, channel));
  169. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_GETFD));
  170. return s48_enter_boolean_2(call, status & FD_CLOEXEC);
  171. }
  172. static s48_ref_t
  173. posix_set_close_on_exec(s48_call_t call, s48_ref_t channel, s48_ref_t value)
  174. {
  175. int status, new_status;
  176. int c_fd;
  177. if (!s48_channel_p_2(call, channel) ||
  178. s48_eq_p_2(call,
  179. s48_channel_status_2(call, channel),
  180. s48_channel_status_closed_2(call)))
  181. s48_assertion_violation_2(call, "posix_set_close_on_exec", "not an open channel", 1, channel);
  182. c_fd = s48_unsafe_extract_long_2(call, s48_unsafe_channel_os_index_2(call, channel));
  183. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_GETFD));
  184. if (s48_extract_boolean_2(call, value))
  185. new_status = status | FD_CLOEXEC;
  186. else
  187. new_status = status & ! FD_CLOEXEC;
  188. if (new_status != status)
  189. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_SETFD, new_status));
  190. return s48_unspecific_2(call);
  191. }
  192. static s48_ref_t
  193. posix_io_flags(s48_call_t call, s48_ref_t channel, s48_ref_t options)
  194. {
  195. int status;
  196. int c_fd;
  197. if (!s48_channel_p_2(call, channel) ||
  198. s48_eq_p_2(call,
  199. s48_channel_status_2(call, channel),
  200. s48_channel_status_closed_2(call)))
  201. s48_assertion_violation_2(call, "posix_io_flags", "not an open channel", 1, channel);
  202. c_fd = s48_unsafe_extract_long_2(call, s48_unsafe_channel_os_index_2(call, channel));
  203. if (s48_false_p_2(call, options)) {
  204. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_GETFL));
  205. return s48_enter_file_options(call, status);
  206. }
  207. else {
  208. int c_options = s48_extract_file_options(call, options);
  209. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_SETFL, c_options));
  210. return s48_unspecific_2(call);
  211. }
  212. }
  213. /* ************************************************************ */
  214. /* File options.
  215. *
  216. * We translate the local bits into our own bits and vice versa.
  217. */
  218. s48_ref_t
  219. s48_enter_file_options(s48_call_t call, int file_options)
  220. {
  221. s48_ref_t sch_file_options;
  222. int my_file_options;
  223. my_file_options =
  224. (O_CREAT & file_options ? 00001 : 0) |
  225. (O_EXCL & file_options ? 00002 : 0) |
  226. (O_NOCTTY & file_options ? 00004 : 0) |
  227. (O_TRUNC & file_options ? 00010 : 0) |
  228. (O_APPEND & file_options ? 00020 : 0) |
  229. /* POSIX 2nd ed., not in Linux
  230. (O_DSYNC & file_options ? 00040 : 0) |
  231. */
  232. (O_NONBLOCK & file_options ? 00100 : 0) |
  233. /* POSIX 2nd ed., not in Linux
  234. (O_RSYNC & file_options ? 00200 : 0) |
  235. */
  236. /* Not in FreeBSD
  237. (O_SYNC & file_options ? 00400 : 0) |
  238. */
  239. (O_RDONLY & file_options ? 01000 : 0) |
  240. (O_RDWR & file_options ? 02000 : 0) |
  241. (O_WRONLY & file_options ? 04000 : 0);
  242. sch_file_options
  243. = s48_integer2enum_set_2(call, posix_file_options_enum_set_type_binding,
  244. my_file_options);
  245. return sch_file_options;
  246. }
  247. int
  248. s48_extract_file_options(s48_call_t call, s48_ref_t sch_file_options)
  249. {
  250. int c_file_options;
  251. long file_options;
  252. s48_check_enum_set_type_2(call, sch_file_options,
  253. posix_file_options_enum_set_type_binding);
  254. file_options = s48_enum_set2integer_2(call, sch_file_options);
  255. c_file_options =
  256. (00001 & file_options ? O_CREAT : 0) |
  257. (00002 & file_options ? O_EXCL : 0) |
  258. (00004 & file_options ? O_NOCTTY : 0) |
  259. (00010 & file_options ? O_TRUNC : 0) |
  260. (00020 & file_options ? O_APPEND : 0) |
  261. /* POSIX 2nd ed., not in Linux
  262. (00040 & file_options ? O_DSYNC : 0) |
  263. */
  264. (00100 & file_options ? O_NONBLOCK : 0) |
  265. /* POSIX 2nd ed., not in Linux
  266. (00200 & file_options ? O_RSYNC : 0) |
  267. */
  268. /* Not in FreeBSD
  269. (00400 & file_options ? O_SYNC : 0) |
  270. */
  271. (01000 & file_options ? O_RDONLY : 0) |
  272. (02000 & file_options ? O_RDWR : 0) |
  273. (04000 & file_options ? O_WRONLY : 0);
  274. return c_file_options;
  275. }