nixio: FreeBSD compatibility #1
[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/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <sys/time.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include "nixio.h"
29
30
31 /**
32  * setblocking()
33  */
34 static int nixio_sock_setblocking(lua_State *L) {
35         int fd = nixio__checkfd(L, 1);
36         luaL_checkany(L, 2);
37         int set = lua_toboolean(L, 2);
38         int flags = fcntl(fd, F_GETFL);
39
40         if (flags == -1) {
41                 return nixio__perror(L);
42         }
43
44         if (!set) {
45                 flags |= O_NONBLOCK;
46         } else {
47                 flags &= ~O_NONBLOCK;
48         }
49
50         return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
51 }
52
53 static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
54         int value;
55         socklen_t optlen = sizeof(value);
56         if (!set) {
57                 if (!getsockopt(fd, level, opt, &value, &optlen)) {
58                         lua_pushinteger(L, value);
59                         return 1;
60                 }
61         } else {
62                 value = luaL_checkinteger(L, set);
63                 if (!setsockopt(fd, level, opt, &value, optlen)) {
64                         lua_pushboolean(L, 1);
65                         return 1;
66                 }
67         }
68         return nixio__perror(L);
69 }
70
71 static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
72         struct linger value;
73         socklen_t optlen = sizeof(value);
74         if (!set) {
75                 if (!getsockopt(fd, level, opt, &value, &optlen)) {
76                         lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
77                         return 1;
78                 }
79         } else {
80                 value.l_linger = luaL_checkinteger(L, set);
81                 value.l_onoff = value.l_linger ? 1 : 0;
82                 if (!setsockopt(fd, level, opt, &value, optlen)) {
83                         lua_pushboolean(L, 1);
84                         return 1;
85                 }
86         }
87         return nixio__perror(L);
88 }
89
90 static int nixio__gso_timev(lua_State *L, int fd, int level, int opt, int set) {
91         struct timeval value;
92         socklen_t optlen = sizeof(value);
93         if (!set) {
94                 if (!getsockopt(fd, level, opt, &value, &optlen)) {
95                         lua_pushinteger(L, value.tv_sec);
96                         lua_pushinteger(L, value.tv_usec);
97                         return 2;
98                 }
99         } else {
100                 value.tv_sec  = luaL_checkinteger(L, set);
101                 value.tv_usec = luaL_optinteger(L, set + 1, 0);
102                 if (!setsockopt(fd, level, opt, &value, optlen)) {
103                         lua_pushboolean(L, 1);
104                         return 1;
105                 }
106         }
107         return nixio__perror(L);
108 }
109
110 /**
111  * get/setsockopt() helper
112  */
113 static int nixio__getsetsockopt(lua_State *L, int set) {
114         nixio_sock *sock = nixio__checksock(L);
115         const char *level = luaL_optlstring(L, 2, "", NULL);
116         const char *option = luaL_optlstring(L, 3, "", NULL);
117         set = (set) ? 4 : 0;
118
119         if (!strcmp(level, "socket")) {
120                 if (!strcmp(option, "keepalive")) {
121                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
122                 } else if (!strcmp(option, "reuseaddr")) {
123                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
124                 } else if (!strcmp(option, "rcvbuf")) {
125                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
126                 } else if (!strcmp(option, "sndbuf")) {
127                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
128                 } else if (!strcmp(option, "priority")) {
129 #ifdef SO_PRIORITY
130                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
131 #else
132                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
133 #endif
134                 } else if (!strcmp(option, "broadcast")) {
135                         return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
136                 } else if (!strcmp(option, "linger")) {
137                         return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
138                 } else if (!strcmp(option, "sndtimeo")) {
139                         return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
140                 } else if (!strcmp(option, "rcvtimeo")) {
141                         return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
142                 } else {
143                         return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
144                          " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo"
145                         );
146                 }
147         } else if (!strcmp(level, "tcp")) {
148                 if (sock->type != SOCK_STREAM) {
149                         return luaL_error(L, "not a TCP socket");
150                 }
151                 if (!strcmp(option, "cork")) {
152 #ifdef TCP_CORK
153                         return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_CORK, set);
154 #else
155                         return nixio__pstatus(L, !(errno = ENOPROTOOPT));
156 #endif
157                 } else if (!strcmp(option, "nodelay")) {
158                         return nixio__gso_int(L, sock->fd, IPPROTO_TCP, TCP_NODELAY, set);
159                 } else {
160                         return luaL_argerror(L, 3, "supported values: cork, nodelay");
161                 }
162         } else {
163                 return luaL_argerror(L, 2, "supported values: socket, tcp");
164         }
165 }
166
167 /**
168  * getsockopt()
169  */
170 static int nixio_sock_getsockopt(lua_State *L) {
171         return nixio__getsetsockopt(L, 0);
172 }
173
174 /**
175  * setsockopt()
176  */
177 static int nixio_sock_setsockopt(lua_State *L) {
178         return nixio__getsetsockopt(L, 1);
179 }
180
181 /* module table */
182 static const luaL_reg M[] = {
183         {"setblocking", nixio_sock_setblocking},
184         {"getsockopt",  nixio_sock_getsockopt},
185         {"setsockopt",  nixio_sock_setsockopt},
186         {NULL,                  NULL}
187 };
188
189 void nixio_open_sockopt(lua_State *L) {
190         lua_pushvalue(L, -2);
191         luaL_register(L, NULL, M);
192         lua_pop(L, 1);
193
194         luaL_getmetatable(L, NIXIO_FILE_META);
195         lua_pushcfunction(L, nixio_sock_setblocking);
196         lua_setfield(L, -2, "setblocking");
197         lua_pop(L, 1);
198 }