1 /*
2 Copyright (C) 2001-2014, Parrot Foundation.
3
4 =head1 NAME
5
6 src/platform/generic/io.c - UNIX IO utility functions
7
8 =head1 DESCRIPTION
9
10 This file implements unbuffered, low-level, UNIX-specific functionality.
11 "UNIX" is a generalization, it may be necessary to create separate OS-specific
12 functions for UNIX flavors.
13
14 These functions are not part of Parrot's API. Don't call them directly, call
15 the C<Parrot_io_*> functions in F<src/io/api.c> instead. Each platform defines
16 the standard set of macros, which call the correct functions for that platform.
17
18 =head2 References:
19
20 APitUE - W. Richard Stevens, AT&T SFIO, Perl 5 (Nick Ing-Simmons)
21
22 =head2 Functions
23
24 =over 4
25
26 =cut
27
28 */
29
30 #include "parrot/parrot.h"
31 #include "../../io/io_private.h"
32
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <unistd.h> /* for pipe() */
36
37 #define DEFAULT_OPEN_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
38
39 #ifndef STDIN_FILENO
40 # define STDIN_FILENO 0
41 #endif
42
43 #ifndef STDOUT_FILENO
44 # define STDOUT_FILENO 1
45 #endif
46
47 #ifndef STDERR_FILENO
48 # define STDERR_FILENO 2
49 #endif
50
51 /* HEADERIZER HFILE: none */
52
53 /* HEADERIZER BEGIN: static */
54 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
55
56 PARROT_CONST_FUNCTION
57 static int convert_flags_to_unix(const INTVAL flags);
58
59 #define ASSERT_ARGS_convert_flags_to_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
60 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
61 /* HEADERIZER END: static */
62
63
64 /*
65
66 =item C<static int convert_flags_to_unix(const INTVAL flags)>
67
68 Returns a UNIX-specific interpretation of C<flags> suitable for passing
69 to C<open()> and C<fopen()> in C<Parrot_io_open_unix()>.
70
71 =cut
72
73 */
74
75 PARROT_CONST_FUNCTION
76 static int
convert_flags_to_unix(const INTVAL flags)77 convert_flags_to_unix(const INTVAL flags)
78 {
79 ASSERT_ARGS(convert_flags_to_unix)
80 int oflags = 0;
81
82 if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) {
83 oflags |= O_RDWR | O_CREAT;
84 }
85 else if (flags & PIO_F_WRITE) {
86 oflags |= O_WRONLY | O_CREAT;
87 }
88 else if (flags & PIO_F_READ) {
89 oflags |= O_RDONLY;
90 }
91
92 if (flags & PIO_F_APPEND) {
93 oflags |= O_APPEND;
94 }
95 else if (flags & PIO_F_TRUNC) {
96 oflags |= O_TRUNC;
97 }
98 return oflags;
99 }
100
101 /*
102
103 =item C<PIOHANDLE Parrot_io_internal_std_os_handle(PARROT_INTERP, const INTVAL
104 fileno)>
105
106 Returns a standard file handle.
107
108 =cut
109
110 */
111
112 PIOHANDLE
Parrot_io_internal_std_os_handle(SHIM_INTERP,const INTVAL fileno)113 Parrot_io_internal_std_os_handle(SHIM_INTERP, const INTVAL fileno)
114 {
115 PIOHANDLE os_handle;
116
117 switch (fileno) {
118 case PIO_STDIN_FILENO:
119 default:
120 os_handle = STDIN_FILENO;
121 break;
122 case PIO_STDOUT_FILENO:
123 os_handle = STDOUT_FILENO;
124 break;
125 case PIO_STDERR_FILENO:
126 os_handle = STDERR_FILENO;
127 break;
128 }
129
130 return os_handle;
131 }
132
133 /*
134
135 =item C<PIOHANDLE Parrot_io_internal_open(PARROT_INTERP, const STRING * const
136 path, const INTVAL flags)>
137
138 Opens a string C<path>. C<flags> is a bitwise C<or> combination of C<PIO_F_*>
139 flag values.
140
141 =cut
142
143 */
144
145 PARROT_WARN_UNUSED_RESULT
146 PIOHANDLE
Parrot_io_internal_open(PARROT_INTERP,ARGIN (const STRING * const path),const INTVAL flags)147 Parrot_io_internal_open(PARROT_INTERP, ARGIN(const STRING * const path), const INTVAL flags)
148 {
149 struct stat buf;
150 PIOHANDLE fd;
151
152 const int oflags = convert_flags_to_unix(flags);
153 const char * const spath = Parrot_str_to_platform_cstring(interp, path);
154
155 while ((fd = open(spath, oflags, DEFAULT_OPEN_MODE)) < 0
156 && errno == EINTR)
157 errno = 0;
158
159 Parrot_str_free_cstring((char*)spath);
160
161 if (fd < 0)
162 return PIO_INVALID_HANDLE;
163
164 /* Don't open directories */
165 if (fstat(fd, &buf) == -1
166 || S_ISDIR(buf.st_mode)) {
167 close(fd);
168 return PIO_INVALID_HANDLE;
169 }
170
171 return fd;
172 }
173
174 /*
175
176 =item C<PIOHANDLE Parrot_io_internal_dup(PARROT_INTERP, const PIOHANDLE handle)>
177
178 Duplicates file handle C<handle>.
179
180 =cut
181
182 */
183
184 PARROT_WARN_UNUSED_RESULT
185 PIOHANDLE
Parrot_io_internal_dup(SHIM_INTERP,const PIOHANDLE handle)186 Parrot_io_internal_dup(SHIM_INTERP, const PIOHANDLE handle)
187 {
188 return dup(handle);
189 }
190
191 /*
192
193 =item C<INTVAL Parrot_io_internal_async(PARROT_INTERP, PMC * const pmc, const
194 INTVAL async)>
195
196 Sets a handle C<*pmc> to blocking or non-blocking mode
197
198 TODO: Change this function signature to take the PIOHANDLE instead of having
199 to query it from the pmc.
200
201 =cut
202
203 */
204
205 PARROT_EXPORT
206 PARROT_WARN_UNUSED_RESULT
207 INTVAL
Parrot_io_internal_async(PARROT_INTERP,ARGMOD (PMC * const pmc),const INTVAL async)208 Parrot_io_internal_async(PARROT_INTERP, ARGMOD(PMC * const pmc), const INTVAL async)
209 {
210 int rflags;
211 PIOHANDLE file_descriptor;
212
213 if (Parrot_io_is_closed(interp, pmc))
214 return 0;
215
216 #if defined(__linux__)
217 file_descriptor = Parrot_io_get_os_handle(interp, pmc);
218
219 if ((rflags = fcntl(file_descriptor, F_GETFL, 0)) >= 0) {
220 if (async)
221 rflags |= O_ASYNC;
222 else
223 rflags &= ~O_ASYNC;
224 if ((rflags = fcntl(file_descriptor, F_SETFL, rflags)) == 0) {
225 if (async)
226 Parrot_io_set_flags(interp, pmc, Parrot_io_get_flags(interp, pmc) | PIO_F_ASYNC);
227 else
228 Parrot_io_set_flags(interp, pmc, Parrot_io_get_flags(interp, pmc) & ~PIO_F_ASYNC);
229 }
230 return rflags;
231 }
232 #else
233 UNUSED(async)
234 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_PIO_NOT_IMPLEMENTED,
235 "Async support not available");
236 #endif
237 return -1;
238 }
239
240 /*
241
242 =item C<INTVAL Parrot_io_internal_close(PARROT_INTERP, const PIOHANDLE
243 file_descriptor)>
244
245 Closes C<*io>'s file descriptor.
246
247 =cut
248
249 */
250
251 INTVAL
Parrot_io_internal_close(SHIM_INTERP,const PIOHANDLE file_descriptor)252 Parrot_io_internal_close(SHIM_INTERP, const PIOHANDLE file_descriptor)
253 {
254 INTVAL result = 0;
255
256 /* BSD and Solaris need explicit fsync() */
257 if (file_descriptor >= 0) {
258 fsync(file_descriptor);
259
260 if (close(file_descriptor) != 0)
261 result = errno;
262 }
263
264 return result;
265 }
266
267
268 /*
269
270 =item C<INTVAL Parrot_io_internal_is_tty(PARROT_INTERP, const PIOHANDLE fd)>
271
272 Returns a boolean value indicating whether C<fd> is a console/tty.
273
274 =cut
275
276 */
277
278 PARROT_WARN_UNUSED_RESULT
279 INTVAL
Parrot_io_internal_is_tty(SHIM_INTERP,const PIOHANDLE fd)280 Parrot_io_internal_is_tty(SHIM_INTERP, const PIOHANDLE fd)
281 {
282 return isatty(fd);
283 }
284
285 /*
286
287 =item C<INTVAL Parrot_io_internal_getblksize(const PIOHANDLE fd)>
288
289 Various ways of determining block size.
290
291 If passed a file descriptor then C<fstat()> and the C<stat> buffer are
292 used if available.
293
294 If called without an argument then the C<BLKSIZE> constant is returned
295 if it was available at compile time, otherwise C<PIO_BLKSIZE> is returned.
296
297 =cut
298
299 */
300
301 PARROT_CONST_FUNCTION
302 INTVAL
Parrot_io_internal_getblksize(const PIOHANDLE fd)303 Parrot_io_internal_getblksize(const PIOHANDLE fd)
304 {
305 if (fd >= 0) {
306 /* Try to get the block size of a regular file */
307 #if 0
308 /*
309 * Is it even worth adding non-portable code here
310 * or should we just estimate a nice buffer size?
311 * Some systems have st_blksize, some don't.
312 */
313 {
314 struct stat sbuf;
315 int err;
316 err = fstat(fd, &sbuf);
317 if (err == 0) {
318 return sbuf.st_blksize;
319 }
320 }
321 #endif
322 }
323 /* Try to determine it from general means. */
324 #ifdef BLKSIZE
325 return BLKSIZE;
326 #else
327 return PIO_BLKSIZE;
328 #endif
329 }
330
331 /*
332
333 =item C<INTVAL Parrot_io_internal_flush(PARROT_INTERP, const PIOHANDLE
334 os_handle)>
335
336 At lowest layer all we can do for C<flush> is to ask the kernel to
337 C<sync()>.
338
339 =cut
340
341 */
342
343 INTVAL
Parrot_io_internal_flush(SHIM_INTERP,const PIOHANDLE os_handle)344 Parrot_io_internal_flush(SHIM_INTERP, const PIOHANDLE os_handle)
345 {
346 return fsync(os_handle);
347 }
348
349 /*
350
351 =item C<size_t Parrot_io_internal_read(PARROT_INTERP, PIOHANDLE const os_handle,
352 char * const buf, const size_t len)>
353
354 Calls C<read()> to return up to C<len> bytes in the memory starting at
355 C<buffer>.
356
357 =cut
358
359 */
360
361 size_t
Parrot_io_internal_read(PARROT_INTERP,PIOHANDLE const os_handle,ARGMOD (char * const buf),const size_t len)362 Parrot_io_internal_read(PARROT_INTERP, PIOHANDLE const os_handle, ARGMOD(char * const buf),
363 const size_t len)
364 {
365
366 for (;;) {
367 const int bytes = read(os_handle, buf, len);
368
369 if (bytes >= 0)
370 return bytes;
371
372 if (errno != EINTR)
373 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
374 "Read error: %s", strerror(errno));
375 }
376 }
377
378 /*
379
380 =item C<size_t Parrot_io_internal_write(PARROT_INTERP, const PIOHANDLE
381 os_handle, const char * const buf, const size_t len)>
382
383 Calls C<write()> to write C<len> bytes from the memory starting at
384 C<buffer> to the file descriptor in C<*io>.
385
386 =cut
387
388 */
389
390 size_t
Parrot_io_internal_write(PARROT_INTERP,const PIOHANDLE os_handle,ARGIN (const char * const buf),const size_t len)391 Parrot_io_internal_write(PARROT_INTERP, const PIOHANDLE os_handle,
392 ARGIN(const char * const buf), const size_t len)
393 {
394 const char *ptr = buf;
395 size_t to_write = len;
396 size_t written = 0;
397
398 while (to_write > 0) {
399 const int count = write(os_handle, ptr, to_write);
400
401 if (count >= 0) {
402 ptr += count;
403 to_write -= count;
404 written += count;
405 }
406 else {
407 switch (errno) {
408 case EINTR:
409 continue;
410 #ifdef EAGAIN
411 case EAGAIN:
412 break;
413 #endif
414 default:
415 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_PIO_ERROR,
416 "Write error: %s", strerror(errno));
417 }
418 }
419 }
420
421 return written;
422 }
423
424 /*
425
426 =item C<PIOOFF_T Parrot_io_internal_seek(PARROT_INTERP, PIOHANDLE const
427 os_handle, const PIOOFF_T offset, const INTVAL whence)>
428
429 Hard seek.
430
431 Calls C<lseek()> to advance the read/write position on C<*io>'s file
432 descriptor to C<offset> bytes from the location indicated by C<whence>.
433
434 =cut
435
436 */
437
438 PIOOFF_T
Parrot_io_internal_seek(SHIM_INTERP,PIOHANDLE const os_handle,const PIOOFF_T offset,const INTVAL whence)439 Parrot_io_internal_seek(SHIM_INTERP, PIOHANDLE const os_handle, const PIOOFF_T offset,
440 const INTVAL whence)
441 {
442 const PIOOFF_T pos = lseek(os_handle, offset, whence);
443 return pos;
444 }
445
446 /*
447
448 =item C<PIOOFF_T Parrot_io_internal_tell(PARROT_INTERP, PIOHANDLE const
449 os_handle)>
450
451 Returns the current read/write position on C<*io>'s file descriptor.
452
453 =cut
454
455 */
456
457 PIOOFF_T
Parrot_io_internal_tell(SHIM_INTERP,PIOHANDLE const os_handle)458 Parrot_io_internal_tell(SHIM_INTERP, PIOHANDLE const os_handle)
459 {
460 const PIOOFF_T pos = lseek(os_handle, (PIOOFF_T)0, SEEK_CUR);
461 return pos;
462 }
463
464 /*
465
466 =item C<PIOHANDLE Parrot_io_internal_open_pipe(PARROT_INTERP, const STRING *
467 const command, const INTVAL flags, INTVAL *pid_out)>
468
469 Very limited C<exec> for now.
470
471 =cut
472
473 */
474
475 PARROT_WARN_UNUSED_RESULT
476 PIOHANDLE
Parrot_io_internal_open_pipe(PARROT_INTERP,ARGIN (const STRING * const command),const INTVAL flags,ARGOUT (INTVAL * pid_out))477 Parrot_io_internal_open_pipe(PARROT_INTERP, ARGIN(const STRING * const command),
478 const INTVAL flags, ARGOUT(INTVAL *pid_out))
479 {
480 PIOHANDLE handles[3];
481
482 if (flags & PIO_F_READ) {
483 *pid_out = Parrot_proc_exec(interp, (STRING*)command, PARROT_EXEC_STDOUT,
484 handles);
485 return handles[1];
486 }
487 else {
488 *pid_out = Parrot_proc_exec(interp, (STRING*)command, PARROT_EXEC_STDIN,
489 handles);
490 return handles[0];
491 }
492 }
493
494 /*
495
496 =item C<INTVAL Parrot_io_internal_pipe(PARROT_INTERP, PIOHANDLE *reader,
497 PIOHANDLE *writer)>
498
499 Uses C<pipe()> to create a matched pair of pipe fds. Returns 0 on success, -1
500 on failure.
501
502 =cut
503
504 */
505
506 PARROT_WARN_UNUSED_RESULT
507 PARROT_CAN_RETURN_NULL
508 INTVAL
Parrot_io_internal_pipe(SHIM_INTERP,ARGMOD (PIOHANDLE * reader),ARGMOD (PIOHANDLE * writer))509 Parrot_io_internal_pipe(SHIM_INTERP, ARGMOD(PIOHANDLE *reader), ARGMOD(PIOHANDLE *writer))
510 {
511 int fds[2];
512 const int rv = pipe(fds);
513
514 if (rv >= 0) {
515 *reader = fds[0];
516 *writer = fds[1];
517 }
518 return rv;
519 }
520
521 /*
522
523 =back
524
525 =head1 SEE ALSO
526
527 F<src/io/api.c>,
528 F<src/io/io_private.h>,
529 F<include/parrot/io.h>.
530
531 =cut
532
533 */
534
535
536 /*
537 * Local variables:
538 * c-file-style: "parrot"
539 * End:
540 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
541 */
542