core: Learned primitive file output module
[lumina.git] / core / src / output / file.c
blobc7b33384689dbe58f394260e2de09cd35f87d1eb
1 #include "output/file.h"
3 #include <errno.h>
4 #include <sys/types.h>
5 #include <sys/sendfile.h>
6 #include <sys/stat.h>
7 #include <unistd.h> /* write */
8 #include <stdlib.h> /* malloc, free */
9 #include <string.h> /* memcpy */
11 static size_t file_get_size(output_item *this);
12 static int file_send(output_item *this, int fd, int fdtype);
13 static size_t file_remaining(output_item *this);
14 static void file_release(output_item *this);
16 static size_t file_get_size(output_item *this) {
17 return ((file_output_item*)this)->size;
20 static int file_send(output_item *this, int fd, int fdtype) {
21 file_output_item *mthis = (file_output_item*)this;
22 /* Zero remaining.. this shouldn't occur in workflow */
23 size_t to_send = file_remaining(this);
24 if(to_send == 0) return 0;
25 /* Expand FDT_SOCKET to use flags (ex: for MSG_MORE) in certain situations (new output_type?) */
26 switch(fdtype) {
27 case FDT_SOCKET: /* might be able to use sendfile here */
28 if(!mthis->no_sendfile) {
29 ssize_t sent = sendfile(fd, mthis->fd, &mthis->index, to_send);
30 if(sent >= 0)
31 return sent;
32 if(!(errno == EINVAL || errno == ENOSYS))
33 return sent;
34 mthis->no_sendfile = 1;
36 case FDT_FILE:
37 case FDT_PIPE: {
38 /* TODO: Need to figure out how to read from buffer async and pipe that to out */
39 char buffer[1024];
40 ssize_t dataRead = read(mthis->fd, buffer, sizeof(buffer));
41 mthis->index += dataRead;
42 int index = 0;
43 while(dataRead > 0) {
44 /* NOTE: Due to nature of data input... output will necessarily block
45 * or go into loop until a non-block op works */
46 ssize_t written = write(fd, buffer + index, dataRead);
47 if(written > 0) {
48 dataRead -= written;
49 index += written;
50 } else {
51 /* Bailed/closed, oh no! */
52 if(errno != EAGAIN || written == 0)
53 return written;
56 return index;
58 default:
59 /* XXX: Raise error */
60 return -1;
64 static size_t file_remaining(output_item *this) {
65 file_output_item *mthis = (file_output_item*)this;
66 return mthis->size - mthis->index;
69 static void file_release(output_item *this) {
70 file_output_item *mthis = (file_output_item*)this;
71 if(mthis->fd)
72 close(mthis->fd);
73 free(this);
76 static output_item_ops file_output_item_ops = {
77 .get_size = file_get_size,
78 .send = file_send,
79 .remaining = file_remaining,
80 .release = file_release
83 /* If an error occurs, fd doesn't changes ownership */
84 output_item *new_file_output_item(int fd) {
85 file_output_item *mitem;
86 struct stat buf;
87 if(0 != fstat(fd, &buf))
88 return NULL;
89 /* FD Must point to a real file */
90 if(!S_ISREG(buf.st_mode))
91 return NULL;
92 mitem = calloc(1, sizeof(file_output_item));
93 if(!mitem) return NULL;
94 mitem->item.ops = &file_output_item_ops;
95 mitem->fd = fd;
96 mitem->size = buf.st_size;
97 mitem->index = 0;
98 return (output_item*)mitem;