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