summaryrefslogtreecommitdiffstats
path: root/lem/streams/sendfile.c
blob: c3395e3077f97132c9e84613bb4ad9f3f2adc29d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
 * This file is part of LEM, a Lua Event Machine.
 * Copyright 2011-2012 Emil Renner Berthing
 *
 * LEM is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * LEM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LEM.  If not, see <http://www.gnu.org/licenses/>.
 */

struct lem_sendfile {
	int fd;
	off_t size;
};

static int
sendfile_open(lua_State *T)
{
	const char *path = luaL_checkstring(T, 1);
	int fd;
	struct stat buf;
	struct lem_sendfile *f;

	fd = open(path, O_RDONLY | O_NONBLOCK);
	if (fd < 0) {
		int err = errno;

		lua_pushnil(T);
		switch (err) {
		case ENOENT:
			lua_pushliteral(T, "not found");
			break;
		case EACCES:
			lua_pushliteral(T, "permission denied");
			break;
		default:
			lua_pushstring(T, strerror(err));
		}
		return 2;
	}

	if (fstat(fd, &buf)) {
		lua_pushnil(T);
		lua_pushstring(T, strerror(errno));
		(void)close(fd);
		return 2;
	}

	/* create userdata and set the metatable */
	f = lua_newuserdata(T, sizeof(struct lem_sendfile));
	lua_pushvalue(T, lua_upvalueindex(1));
	lua_setmetatable(T, -2);

	/* initialize userdata */
	f->fd = fd;
	f->size = buf.st_size;

	return 1;
}

static int
sendfile_gc(lua_State *T)
{
	struct lem_sendfile *f = lua_touserdata(T, 1);

	if (f->fd < 0)
		return 0;

	(void)close(f->fd);
	return 0;
}

static int
sendfile_close(lua_State *T)
{
	struct lem_sendfile *f;

	luaL_checktype(T, 1, LUA_TUSERDATA);
	f = lua_touserdata(T, 1);
	if (f->fd < 0) {
		lua_pushnil(T);
		lua_pushliteral(T, "already closed");
		return 2;
	}

	if (close(f->fd)) {
		lua_pushnil(T);
		lua_pushstring(T, strerror(errno));
		return 2;
	}

	f->fd = -1;
	lua_pushboolean(T, 1);
	return 1;
}

static int
sendfile_size(lua_State *T)
{
	struct lem_sendfile *f;

	luaL_checktype(T, 1, LUA_TUSERDATA);
	f = lua_touserdata(T, 1);
	if (f->fd < 0) {
		lua_pushnil(T);
		lua_pushliteral(T, "closed");
		return 2;
	}

	lua_pushnumber(T, (lua_Number)f->size);
	return 1;
}