nixio: Fixes, use POSIX calls for file i/o
[project/luci.git] / libs / nixio / src / sockopt.c
1 /*
2 * nixio - Linux I/O library for lua
3 *
4 * Copyright (C) 2009 Steven Barth <steven@midlink.org>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include "nixio.h"
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <netinet/tcp.h>
23 #include <sys/time.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include "nixio.h"
27
28 /**
29 * setblocking()
30 */
31 static int nixio_sock_setblocking(lua_State *L) {
32 int fd = nixio__checkfd(L, 1);
33 luaL_checkany(L, 2);
34 int set = lua_toboolean(L, 2);
35 int flags = fcntl(fd, F_GETFL);
36
37 if (flags == -1) {
38 return nixio__perror(L);
39 }
40
41 if (!set) {
42 flags |= O_NONBLOCK;
43 } else {
44 flags &= ~O_NONBLOCK;
45 }
46
47 return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
48 }
49
50 static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
51 int value;
52 socklen_t optlen = sizeof(value);
53 if (!set) {
54 if (!getsockopt(fd, level, opt, &value, &optlen)) {
55 lua_pushinteger(L, value);
56 return 1;
57 }
58 } else {
59 value = luaL_checkinteger(L, set);
60 if (!setsockopt(fd, level, opt, &value, optlen)) {
61 lua_pushboolean(L, 1);
62 return 1;
63 }
64 }
65 return nixio__perror(L);
66 }
67
68 static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
69 struct linger value;
70 socklen_t optlen = sizeof(value);
71 if (!set) {
72 if (!getsockopt(fd, level, opt, &value, &optlen)) {
73 lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
74 return 1;
75 }
76 } else {
77 value.l_linger = luaL_checkinteger(L, set);
78 value.l_onoff = value.l_linger ? 1 : 0;
79 if (!setsockopt(fd, level, opt, &value, optlen)) {
80 lua_pushboolean(L, 1);
81 return 1;
82 }
83 }
84 return nixio__perror(L);
85 }
86
87 static int nixio__gso_timev(lua_State *L, int fd, int level, int opt, int set) {
88 struct timeval value;
89 socklen_t optlen = sizeof(value);
90 if (!set) {
91 if (!getsockopt(fd, level, opt, &value, &optlen)) {
92 lua_pushinteger(L, value.tv_sec);
93 lua_pushinteger(L, value.tv_usec);
94 return 2;
95 }
96 } else {
97 value.tv_sec = luaL_checkinteger(L, set);
98 value.tv_usec = luaL_optinteger(L, set + 1, 0);
99 if (!setsockopt(fd, level, opt, &value, optlen)) {
100 lua_pushboolean(L, 1);
101 return 1;
102 }
103 }
104 return nixio__perror(L);
105 }
106
107 /**
108 * get/setsockopt() helper
109 */
110 static int nixio__getsetsockopt(lua_State *L, int set) {
111 nixio_sock *sock = nixio__checksock(L);
112 const char *level = luaL_optlstring(L, 2, "", NULL);
113 const char *option = luaL_optlstring(L, 3, "", NULL);
114 set = (set) ? 4 : 0;
115
116 if (!strcmp(level, "socket")) {
117 if (!strcmp(option, "keepalive")) {
118 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
119 } else if (!strcmp(option, "reuseaddr")) {
120 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
121 } else if (!strcmp(option, "rcvbuf")) {
122 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
123 } else if (!strcmp(option, "sndbuf")) {
124 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
125 } else if (!strcmp(option, "priority")) {
126 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
127 } else if (!strcmp(option, "broadcast")) {
128 return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
129 } else if (!strcmp(option, "linger")) {
130 return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
131 } else if (!strcmp(option, "sndtimeo")) {
132 return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
133 } else if (!strcmp(option, "rcvtimeo")) {
134 return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
135 } else {
136 return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
137 " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo"
138 );
139 }
140 } else if (!strcmp(level, "tcp")) {
141 if (sock->type != SOCK_STREAM) {
142 return luaL_error(L, "not a TCP socket");
143 }
144 if (!strcmp(option, "cork")) {
145 return nixio__gso_int(L, sock->fd, SOL_TCP, TCP_CORK, set);
146 } else if (!strcmp(option, "nodelay")) {
147 return nixio__gso_int(L, sock->fd, SOL_TCP, TCP_NODELAY, set);
148 } else {
149 return luaL_argerror(L, 3, "supported values: cork, nodelay");
150 }
151 } else {
152 return luaL_argerror(L, 2, "supported values: socket, tcp");
153 }
154 }
155
156 /**
157 * getsockopt()
158 */
159 static int nixio_sock_getsockopt(lua_State *L) {
160 return nixio__getsetsockopt(L, 0);
161 }
162
163 /**
164 * setsockopt()
165 */
166 static int nixio_sock_setsockopt(lua_State *L) {
167 return nixio__getsetsockopt(L, 1);
168 }
169
170 /* module table */
171 static const luaL_reg M[] = {
172 {"setblocking", nixio_sock_setblocking},
173 {"getsockopt", nixio_sock_getsockopt},
174 {"setsockopt", nixio_sock_setsockopt},
175 {NULL, NULL}
176 };
177
178 void nixio_open_sockopt(lua_State *L) {
179 lua_pushvalue(L, -2);
180 luaL_register(L, NULL, M);
181 lua_pop(L, 1);
182
183 luaL_getmetatable(L, NIXIO_FILE_META);
184 lua_pushcfunction(L, nixio_sock_setblocking);
185 lua_setfield(L, -2, "setblocking");
186 lua_pop(L, 1);
187 }