2 Copyright (C) 2001-2010, Parrot Foundation.
7 src/io/unix.c - UNIX IO utility functions
11 This file implements unbuffered, low-level, UNIX-specific functionality.
12 "UNIX" is a generalization, it may be necessary to create separate OS-specific
13 functions for UNIX flavors.
15 These functions are not part of Parrot's API. Don't call them directly, call
16 the C<Parrot_io_*> functions in F<src/io/api.c> instead. Each platform defines
17 the standard set of macros, which call the correct functions for that platform.
21 APitUE - W. Richard Stevens, AT&T SFIO, Perl 5 (Nick Ing-Simmons)
31 #include "parrot/parrot.h"
32 #include "io_private.h"
33 #include "pmc/pmc_filehandle.h"
37 # include <sys/types.h>
38 # include <sys/wait.h>
39 # include <unistd.h> /* for pipe() */
41 /* HEADERIZER HFILE: include/parrot/io_unix.h */
43 /* HEADERIZER BEGIN: static */
44 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
47 static int convert_flags_to_unix(INTVAL flags
);
49 static INTVAL
io_is_tty_unix(PIOHANDLE fd
);
50 #define ASSERT_ARGS_convert_flags_to_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
51 #define ASSERT_ARGS_io_is_tty_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
52 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
53 /* HEADERIZER END: static */
58 =item C<static int convert_flags_to_unix(INTVAL flags)>
60 Returns a UNIX-specific interpretation of C<flags> suitable for passing
61 to C<open()> and C<fopen()> in C<Parrot_io_open_unix()> and
62 C<Parrot_io_fdopen_unix()> respectively.
70 convert_flags_to_unix(INTVAL flags
)
72 ASSERT_ARGS(convert_flags_to_unix
)
75 if ((flags
& (PIO_F_WRITE
| PIO_F_READ
)) == (PIO_F_WRITE
| PIO_F_READ
)) {
76 oflags
|= O_RDWR
| O_CREAT
;
78 else if (flags
& PIO_F_WRITE
) {
79 oflags
|= O_WRONLY
| O_CREAT
;
81 else if (flags
& PIO_F_READ
) {
85 if (flags
& PIO_F_APPEND
) {
88 else if (flags
& PIO_F_TRUNC
) {
96 =item C<INTVAL Parrot_io_init_unix(PARROT_INTERP)>
98 Sets up the interpreter's standard C<std*> IO handles. Returns C<0> on
99 success and C<-1> on error.
106 Parrot_io_init_unix(PARROT_INTERP
)
108 ASSERT_ARGS(Parrot_io_init_unix
)
109 ParrotIOData
* const d
= interp
->piodata
;
110 if (d
!= NULL
&& d
->table
!= NULL
) {
113 filehandle
= Parrot_io_fdopen_unix(interp
, PMCNULL
, STDIN_FILENO
, PIO_F_READ
);
114 if (PMC_IS_NULL(filehandle
))
116 _PIO_STDIN(interp
) = filehandle
;
118 filehandle
= Parrot_io_fdopen_unix(interp
, PMCNULL
, STDOUT_FILENO
, PIO_F_WRITE
);
119 if (PMC_IS_NULL(filehandle
))
121 _PIO_STDOUT(interp
) = filehandle
;
123 filehandle
= Parrot_io_fdopen_unix(interp
, PMCNULL
, STDERR_FILENO
, PIO_F_WRITE
);
124 if (PMC_IS_NULL(filehandle
))
126 _PIO_STDERR(interp
) = filehandle
;
136 =item C<PMC * Parrot_io_open_unix(PARROT_INTERP, PMC *filehandle, STRING *path,
139 Opens a string C<path>. C<flags> is a bitwise C<or> combination of C<PIO_F_*>
146 PARROT_WARN_UNUSED_RESULT
147 PARROT_CAN_RETURN_NULL
149 Parrot_io_open_unix(PARROT_INTERP
, ARGMOD_NULLOK(PMC
*filehandle
),
150 ARGIN(STRING
*path
), INTVAL flags
)
152 ASSERT_ARGS(Parrot_io_open_unix
)
157 if (flags
& PIO_F_PIPE
)
158 return Parrot_io_open_pipe_unix(interp
, filehandle
, path
, flags
);
160 if ((flags
& (PIO_F_WRITE
| PIO_F_READ
)) == 0)
161 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
162 "Invalid mode for file open");
164 oflags
= convert_flags_to_unix(flags
);
165 spath
= Parrot_str_to_cstring(interp
, path
);
167 /* Only files for now */
170 /* Try open with no create first */
171 while ((fd
= open(spath
, oflags
& (O_WRONLY
| O_RDWR
| O_APPEND
), DEFAULT_OPEN_MODE
))
172 < 0 && errno
== EINTR
)
178 * Now check if we specified O_CREAT|O_EXCL or not.
179 * If so, we must return NULL, else either use the
180 * descriptor or create the file.
182 if ((oflags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
)) {
185 /* returning before C string freed */
186 Parrot_str_free_cstring(spath
);
190 /* Check for truncate? */
191 if (oflags
& O_TRUNC
) {
193 while ((tfd
= creat(spath
, PIO_DEFAULTMODE
)) < 0 && errno
== EINTR
)
199 else if (oflags
& O_CREAT
) {
200 /* O_CREAT and file doesn't exist. */
201 while ((fd
= creat(spath
, PIO_DEFAULTMODE
)) < 0 && errno
== EINTR
)
203 if (!(oflags
& O_WRONLY
)) {
207 /* File created, reopen with read+write */
208 while ((fd
= open(spath
, oflags
& (O_WRONLY
| O_RDWR
),
209 DEFAULT_OPEN_MODE
)) < 0 && errno
== EINTR
)
214 /* File doesn't exist and O_CREAT not specified */
217 Parrot_str_free_cstring(spath
); /* done with C string */
221 if (fstat(fd
, &buf
) == -1) {
225 if ((buf
.st_mode
& S_IFMT
) == S_IFDIR
) {
230 /* Set generic flag here if is a terminal then
231 * FileHandle can know how to setup buffering.
232 * STDIN, STDOUT, STDERR would be in this case
233 * so we would setup linebuffering.
235 if (io_is_tty_unix(fd
))
236 flags
|= PIO_F_CONSOLE
;
238 if (PMC_IS_NULL(filehandle
)) {
239 PMC
* const io
= Parrot_io_new_pmc(interp
, flags
);
240 Parrot_io_set_os_handle(interp
, io
, fd
);
244 Parrot_io_set_flags(interp
, filehandle
, flags
);
245 Parrot_io_set_os_handle(interp
, filehandle
, fd
);
252 # if PARROT_ASYNC_DEVEL
256 =item C<INTVAL Parrot_io_async_unix(PARROT_INTERP, PMC *filehandle, INTVAL b)>
258 Experimental asynchronous IO.
260 This is available if C<PARROT_ASYNC_DEVEL> is defined.
262 Only works on Linux at the moment.
264 Toggles the C<O_ASYNC> flag on the IO file descriptor.
271 Parrot_io_async_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
), INTVAL b
)
273 ASSERT_ARGS(Parrot_io_async_unix
)
276 PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
278 if ((rflags
= fcntl(file_descriptor
, F_GETFL
, 0)) >= 0) {
283 return fcntl(file_descriptor
, F_SETFL
, rflags
);
286 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_PIO_NOT_IMPLEMENTED
,
287 "Async support not available");
296 =item C<PMC * Parrot_io_fdopen_unix(PARROT_INTERP, PMC *filehandle, PIOHANDLE
299 Returns a new C<FileHandle> PMC with the file descriptor passed in.
305 PARROT_WARN_UNUSED_RESULT
306 PARROT_CANNOT_RETURN_NULL
308 Parrot_io_fdopen_unix(PARROT_INTERP
, ARGMOD_NULLOK(PMC
*filehandle
), PIOHANDLE fd
, INTVAL flags
)
310 ASSERT_ARGS(Parrot_io_fdopen_unix
)
311 if (io_is_tty_unix(fd
))
312 flags
|= PIO_F_CONSOLE
;
314 /* fdopened files are always shared */
315 flags
|= PIO_F_SHARED
;
317 if (PMC_IS_NULL(filehandle
)) {
318 PMC
* const io
= Parrot_io_new_pmc(interp
, flags
);
319 Parrot_io_set_os_handle(interp
, io
, fd
);
323 Parrot_io_set_flags(interp
, filehandle
, flags
);
324 Parrot_io_set_os_handle(interp
, filehandle
, fd
);
331 =item C<INTVAL Parrot_io_close_unix(PARROT_INTERP, PMC *filehandle)>
333 Closes C<*io>'s file descriptor.
340 Parrot_io_close_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
))
342 ASSERT_ARGS(Parrot_io_close_unix
)
344 const PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
345 const int flags
= Parrot_io_get_flags(interp
, filehandle
);
347 /* BSD and Solaris need explicit fsync() */
348 if (file_descriptor
>= 0) {
349 fsync(file_descriptor
);
350 if (close(file_descriptor
) != 0)
353 /* Wait for the child after closing the
354 * handle, to let it notice the closing and finish */
355 if (flags
& PIO_F_PIPE
) {
357 waitpid(VTABLE_get_integer_keyed_int(interp
, filehandle
, 0), &status
, 0);
358 if (WIFEXITED(status
)) {
359 SETATTR_FileHandle_exit_status(interp
, filehandle
, WEXITSTATUS(status
));
362 /* abnormal termination means non-zero exit status */
363 SETATTR_FileHandle_exit_status(interp
, filehandle
, 1);
367 Parrot_io_set_os_handle(interp
, filehandle
, -1);
374 =item C<INTVAL Parrot_io_close_piohandle_unix(PARROT_INTERP, PIOHANDLE handle)>
376 Closes the given file descriptor. Returns 0 on success, -1 on error.
383 Parrot_io_close_piohandle_unix(SHIM_INTERP
, PIOHANDLE handle
)
385 ASSERT_ARGS(Parrot_io_close_piohandle_unix
)
386 return close(handle
);
391 =item C<INTVAL Parrot_io_is_closed_unix(PARROT_INTERP, const PMC *filehandle)>
393 Test whether the filehandle has been closed.
400 Parrot_io_is_closed_unix(PARROT_INTERP
, ARGIN(const PMC
*filehandle
))
402 ASSERT_ARGS(Parrot_io_is_closed_unix
)
403 if (Parrot_io_get_os_handle(interp
, filehandle
) == -1)
411 =item C<static INTVAL io_is_tty_unix(PIOHANDLE fd)>
413 Returns a boolean value indicating whether C<fd> is a console/tty.
420 io_is_tty_unix(PIOHANDLE fd
)
422 ASSERT_ARGS(io_is_tty_unix
)
428 =item C<INTVAL Parrot_io_getblksize_unix(PIOHANDLE fd)>
430 Various ways of determining block size.
432 If passed a file descriptor then C<fstat()> and the C<stat> buffer are
435 If called without an argument then the C<BLKSIZE> constant is returned
436 if it was available at compile time, otherwise C<PIO_BLKSIZE> is returned.
443 Parrot_io_getblksize_unix(PIOHANDLE fd
)
445 ASSERT_ARGS(Parrot_io_getblksize_unix
)
447 /* Try to get the block size of a regular file */
450 * Is it even worth adding non-portable code here
451 * or should we just estimate a nice buffer size?
452 * Some systems have st_blksize, some don't.
457 err
= fstat(fd
, &sbuf
);
459 return sbuf
.st_blksize
;
464 /* Try to determine it from general means. */
474 =item C<INTVAL Parrot_io_flush_unix(PARROT_INTERP, PMC *filehandle)>
476 At lowest layer all we can do for C<flush> is to ask the kernel to
479 XXX: Is it necessary to C<sync()> here?
486 Parrot_io_flush_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
))
488 ASSERT_ARGS(Parrot_io_flush_unix
)
489 const PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
490 return fsync(file_descriptor
);
495 =item C<size_t Parrot_io_read_unix(PARROT_INTERP, PMC *filehandle, STRING
498 Calls C<read()> to return up to C<len> bytes in the memory starting at
506 Parrot_io_read_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
),
509 ASSERT_ARGS(Parrot_io_read_unix
)
510 const PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
511 const INTVAL file_flags
= Parrot_io_get_flags(interp
, filehandle
);
512 STRING
* const s
= Parrot_io_make_string(interp
, buf
, 2048);
514 const size_t len
= s
->bufused
;
515 void * const buffer
= Buffer_bufstart(s
);
518 const int bytes
= read(file_descriptor
, buffer
, len
);
520 s
->bufused
= s
->strlen
= bytes
;
523 else if (bytes
< 0) {
528 s
->bufused
= s
->strlen
= 0;
533 /* Read returned 0, EOF if len requested > 0 */
534 if (len
> 0 || (file_flags
& PIO_F_LINEBUF
))
535 Parrot_io_set_flags(interp
, filehandle
, (file_flags
| PIO_F_EOF
));
536 s
->bufused
= s
->strlen
= 0;
544 =item C<size_t Parrot_io_write_unix(PARROT_INTERP, PMC *filehandle, const STRING
547 Calls C<write()> to write C<len> bytes from the memory starting at
548 C<buffer> to the file descriptor in C<*io>.
555 Parrot_io_write_unix(PARROT_INTERP
, ARGIN(PMC
*filehandle
), ARGIN(const STRING
*s
))
557 ASSERT_ARGS(Parrot_io_write_unix
)
558 const PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
559 const char * const buffer
= s
->strstart
;
560 const char * ptr
= buffer
;
562 size_t to_write
= s
->bufused
;
566 while (to_write
> 0) {
567 const int err
= write(file_descriptor
, ptr
, to_write
);
591 =item C<PIOOFF_T Parrot_io_seek_unix(PARROT_INTERP, PMC *filehandle, PIOOFF_T
592 offset, INTVAL whence)>
596 Calls C<lseek()> to advance the read/write position on C<*io>'s file
597 descriptor to C<offset> bytes from the location indicated by C<whence>.
604 Parrot_io_seek_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
),
605 PIOOFF_T offset
, INTVAL whence
)
607 ASSERT_ARGS(Parrot_io_seek_unix
)
608 PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
609 const PIOOFF_T pos
= lseek(file_descriptor
, offset
, whence
);
614 if (offset
> Parrot_io_get_file_size(interp
, filehandle
)) {
615 Parrot_io_set_file_size(interp
, filehandle
, offset
);
620 const PIOOFF_T avail
= offset
621 + Parrot_io_get_buffer_next(interp
, filehandle
)
622 - Parrot_io_get_buffer_start(interp
, filehandle
);
623 if (avail
> Parrot_io_get_file_size(interp
, filehandle
)) {
624 Parrot_io_set_file_size(interp
, filehandle
, avail
);
633 Parrot_io_set_file_position(interp
, filehandle
, pos
);
635 /* Seek clears EOF */
636 Parrot_io_set_flags(interp
, filehandle
,
637 (Parrot_io_get_flags(interp
, filehandle
) & ~PIO_F_EOF
));
643 =item C<PIOOFF_T Parrot_io_tell_unix(PARROT_INTERP, PMC *filehandle)>
645 Returns the current read/write position on C<*io>'s file discriptor.
652 Parrot_io_tell_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
))
654 ASSERT_ARGS(Parrot_io_tell_unix
)
655 PIOHANDLE file_descriptor
= Parrot_io_get_os_handle(interp
, filehandle
);
656 const PIOOFF_T pos
= lseek(file_descriptor
, (PIOOFF_T
)0, SEEK_CUR
);
663 =item C<PMC * Parrot_io_open_pipe_unix(PARROT_INTERP, PMC *filehandle, STRING
664 *command, int flags)>
666 Very limited C<exec> for now.
672 PARROT_WARN_UNUSED_RESULT
673 PARROT_CAN_RETURN_NULL
675 Parrot_io_open_pipe_unix(PARROT_INTERP
, ARGMOD(PMC
*filehandle
),
676 ARGIN(STRING
*command
), int flags
)
678 ASSERT_ARGS(Parrot_io_open_pipe_unix
)
680 * pipe(), fork() should be defined, if this header is present
681 * if that's not true, we need a test
683 # ifdef PARROT_HAS_HEADER_UNISTD
686 const int f_read
= (flags
& PIO_F_READ
) != 0;
687 const int f_write
= (flags
& PIO_F_WRITE
) != 0;
688 if (f_read
== f_write
)
689 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_PIO_ERROR
,
690 "Invalid pipe mode: %X", flags
);
693 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_PIO_ERROR
,
694 "Error opening pipe: %s", strerror(errno
));
698 /* fork failed, cleaning up */
701 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_PIO_ERROR
,
702 "fork failed: %s", strerror(errno
));
705 /* Parent - return IO stream */
707 if (PMC_IS_NULL(filehandle
))
708 io
= Parrot_io_new_pmc(interp
, flags
& (PIO_F_READ
|PIO_F_WRITE
));
712 /* Save the pid of the child, we'll wait for it when closing */
713 VTABLE_set_integer_keyed_int(interp
, io
, 0, pid
);
716 /* close this writer's end of pipe */
718 Parrot_io_set_os_handle(interp
, io
, fds
[0]);
720 else { /* assume write only for now */
721 /* close this reader's end */
723 Parrot_io_set_os_handle(interp
, io
, fds
[1]);
727 else /* (pid == 0) */ {
728 /* Child - exec process */
730 /* C strings for the execv call defined without const to avoid
731 * const problems without copying them.
732 * Please don't change this without testing with a c++ compiler.
734 static char auxarg0
[] = "/bin/sh";
735 static char auxarg1
[] = "-c";
738 /* the other end is writing - we read from the pipe */
742 if (Parrot_dup(fds
[0]) != STDIN_FILENO
)
746 /* XXX redirect stdout, stderr to pipe */
747 close(STDOUT_FILENO
);
748 close(STDERR_FILENO
);
751 if (Parrot_dup(fds
[1]) != STDOUT_FILENO
)
753 if (Parrot_dup(fds
[1]) != STDERR_FILENO
)
759 argv
[2] = Parrot_str_to_cstring(interp
, command
);
761 execv(argv
[0], argv
);
763 /* Will never reach this unless exec fails.
764 * No need to clean up, we're just going to exit */
773 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_UNIMPLEMENTED
,
774 "pipe() unimplemented");
780 =item C<size_t Parrot_io_peek_unix(PARROT_INTERP, PMC *filehandle, STRING
783 Retrieve the next character in the stream without modifying the stream. Not
784 implemented on this platform.
791 Parrot_io_peek_unix(PARROT_INTERP
,
792 SHIM(PMC
*filehandle
),
795 ASSERT_ARGS(Parrot_io_peek_unix
)
796 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_UNIMPLEMENTED
,
797 "peek() not implemented");
803 =item C<INTVAL Parrot_io_pipe_unix(PARROT_INTERP, PIOHANDLE *reader, PIOHANDLE
806 Uses C<pipe()> to create a matched pair of pipe fds. Returns 0 on success, -1
813 PARROT_WARN_UNUSED_RESULT
814 PARROT_CAN_RETURN_NULL
816 Parrot_io_pipe_unix(SHIM_INTERP
, ARGMOD(PIOHANDLE
*reader
), ARGMOD(PIOHANDLE
*writer
))
818 ASSERT_ARGS(Parrot_io_pipe_unix
)
820 const int rv
= pipe(fds
);
829 #endif /* PIO_OS_UNIX */
840 F<src/io/io_private.h>,
841 F<include/parrot/io_unix.h>.
850 * c-file-style: "parrot"
852 * vim: expandtab shiftwidth=4: