Merge pull request #278 from nmav/ocserv
[project/luci.git] / libs / nixio / src / file.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 <errno.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/param.h>
29
30
31 static int nixio_open(lua_State *L) {
32         const char *filename = luaL_checklstring(L, 1, NULL);
33         int flags;
34
35         if (lua_isnoneornil(L, 2)) {
36                 flags = O_RDONLY;
37         } else if (lua_isnumber(L, 2)) {
38                 flags = lua_tointeger(L, 2);
39         } else if (lua_isstring(L, 2)) {
40                 const char *str = lua_tostring(L, 2);
41                 if (!strcmp(str, "r")) {
42                         flags = O_RDONLY;
43                 } else if (!strcmp(str, "r+")) {
44                         flags = O_RDWR;
45                 } else if (!strcmp(str, "w")) {
46                         flags = O_WRONLY | O_CREAT | O_TRUNC;
47                 } else if (!strcmp(str, "w+")) {
48                         flags = O_RDWR | O_CREAT | O_TRUNC;
49                 } else if (!strcmp(str, "a")) {
50                         flags = O_WRONLY | O_CREAT | O_APPEND;
51                 } else if (!strcmp(str, "a+")) {
52                         flags = O_RDWR | O_CREAT | O_APPEND;
53                 } else {
54                         return luaL_argerror(L, 2, "supported values: r, r+, w, w+, a, a+");
55                 }
56         } else {
57                 return luaL_argerror(L, 2, "open flags or string expected");
58         }
59
60         int fd;
61
62         do {
63                 fd = open(filename, flags, nixio__check_mode(L, 3, 0666));
64         } while (fd == -1 && errno == EINTR);
65         if (fd == -1) {
66                 return nixio__perror(L);
67         }
68
69         int *udata = lua_newuserdata(L, sizeof(int));
70         if (!udata) {
71                 return luaL_error(L, "out of memory");
72         }
73
74         *udata = fd;
75
76         luaL_getmetatable(L, NIXIO_FILE_META);
77         lua_setmetatable(L, -2);
78
79         return 1;
80 }
81
82 static int nixio_open_flags(lua_State *L) {
83         int mode = 0;
84         const int j = lua_gettop(L);
85         for (int i=1; i<=j; i++) {
86                 const char *flag = luaL_checkstring(L, i);
87                 if (!strcmp(flag, "append")) {
88                         mode |= O_APPEND;
89                 } else if (!strcmp(flag, "creat")) {
90                         mode |= O_CREAT;
91                 } else if (!strcmp(flag, "excl")) {
92                         mode |= O_EXCL;
93                 } else if (!strcmp(flag, "nonblock") || !strcmp(flag, "ndelay")) {
94 #ifndef __WINNT__
95                         mode |= O_NONBLOCK;
96 #endif
97                 } else if (!strcmp(flag, "sync")) {
98 #ifndef __WINNT__
99                         mode |= O_SYNC;
100 #endif
101                 } else if (!strcmp(flag, "trunc")) {
102                         mode |= O_TRUNC;
103                 } else if (!strcmp(flag, "rdonly")) {
104                         mode |= O_RDONLY;
105                 } else if (!strcmp(flag, "wronly")) {
106                         mode |= O_WRONLY;
107                 } else if (!strcmp(flag, "rdwr")) {
108                         mode |= O_RDWR;
109                 } else {
110                         return luaL_argerror(L, i, "supported values: append, creat, "
111                                         "excl, nonblock, ndelay, sync, trunc");
112                 }
113         }
114         lua_pushinteger(L, mode);
115         return 1;
116 }
117
118 static int nixio_dup(lua_State *L) {
119         int oldfd = nixio__checkfd(L, 1);
120         int newfd = (lua_gettop(L) > 1) ? nixio__checkfd(L, 2) : -1;
121         int stat  = (newfd == -1) ? dup(oldfd) : dup2(oldfd, newfd);
122
123         if (stat == -1) {
124                 return nixio__perror(L);
125         } else {
126                 if (newfd == -1) {
127                         int *udata = lua_newuserdata(L, sizeof(int));
128                         if (!udata) {
129                                 return luaL_error(L, "out of memory");
130                         }
131
132                         *udata = stat;
133                         luaL_getmetatable(L, NIXIO_FILE_META);
134                         lua_setmetatable(L, -2);
135                 } else {
136                         lua_pushvalue(L, 2);
137                 }
138                 return 1;
139         }
140 }
141
142 static int nixio_pipe(lua_State *L) {
143         int pipefd[2], *udata;
144         if (pipe(pipefd)) {
145                 return nixio__perror(L);
146         }
147
148         luaL_getmetatable(L, NIXIO_FILE_META);
149         udata = lua_newuserdata(L, sizeof(int));
150         if (!udata) {
151                 return luaL_error(L, "out of memory");
152         }
153
154         *udata = pipefd[0];
155         lua_pushvalue(L, -2);
156         lua_setmetatable(L, -2);
157
158
159         udata = lua_newuserdata(L, sizeof(int));
160         if (!udata) {
161                 return luaL_error(L, "out of memory");
162         }
163
164         *udata = pipefd[1];
165         lua_pushvalue(L, -3);
166         lua_setmetatable(L, -2);
167
168         return 2;
169 }
170
171 static int nixio_file_write(lua_State *L) {
172         int fd = nixio__checkfd(L, 1);
173         size_t len;
174         ssize_t sent;
175         const char *data = luaL_checklstring(L, 2, &len);
176
177         if (lua_gettop(L) > 2) {
178                 int offset = luaL_optint(L, 3, 0);
179                 if (offset) {
180                         if (offset < len) {
181                                 data += offset;
182                                 len -= offset;
183                         } else {
184                                 len = 0;
185                         }
186                 }
187
188                 unsigned int wlen = luaL_optint(L, 4, len);
189                 if (wlen < len) {
190                         len = wlen;
191                 }
192         }
193
194         do {
195                 sent = write(fd, data, len);
196         } while(sent == -1 && errno == EINTR);
197         if (sent >= 0) {
198                 lua_pushinteger(L, sent);
199                 return 1;
200         } else {
201                 return nixio__perror(L);
202         }
203 }
204
205 static int nixio_file_read(lua_State *L) {
206         int fd = nixio__checkfd(L, 1);
207         char buffer[NIXIO_BUFFERSIZE];
208         uint req = luaL_checkinteger(L, 2);
209         int readc;
210
211         /* We limit the readsize to NIXIO_BUFFERSIZE */
212         req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req;
213
214         do {
215                 readc = read(fd, buffer, req);
216         } while (readc == -1 && errno == EINTR);
217
218         if (readc < 0) {
219                 return nixio__perror(L);
220         } else {
221                 lua_pushlstring(L, buffer, readc);
222                 return 1;
223         }
224 }
225
226
227 static int nixio_file_seek(lua_State *L) {
228         int fd = nixio__checkfd(L, 1);
229         off_t len = (off_t)nixio__checknumber(L, 2);
230         int whence;
231         const char *whstr = luaL_optlstring(L, 3, "set", NULL);
232         if (!strcmp(whstr, "set")) {
233                 whence = SEEK_SET;
234         } else if (!strcmp(whstr, "cur")) {
235                 whence = SEEK_CUR;
236         } else if (!strcmp(whstr, "end")) {
237                 whence = SEEK_END;
238         } else {
239                 return luaL_argerror(L, 3, "supported values: set, cur, end");
240         }
241         len = lseek(fd, len, whence);
242         if (len == -1) {
243                 return nixio__perror(L);
244         } else {
245                 nixio__pushnumber(L, len);
246                 return 1;
247         }
248 }
249
250 static int nixio_file_tell(lua_State *L) {
251         int fd = nixio__checkfd(L, 1);
252         off_t pos = lseek(fd, 0, SEEK_CUR);
253         if (pos < 0) {
254                 return nixio__perror(L);
255         } else {
256                 nixio__pushnumber(L, pos);
257                 return 1;
258         }
259 }
260
261 static int nixio_file_stat(lua_State *L) {
262         nixio_stat_t buf;
263         if (fstat(nixio__checkfd(L, 1), &buf)) {
264                 return nixio__perror(L);
265         } else {
266                 nixio__push_stat(L, &buf);
267                 if (lua_isstring(L, 2)) {
268                         lua_getfield(L, -1, lua_tostring(L, 2));
269                 }
270                 return 1;
271         }
272 }
273
274 static int nixio_file_sync(lua_State *L) {
275         int fd = nixio__checkfd(L, 1);
276         int stat;
277 #if (!defined BSD && !defined __WINNT__)
278         int dataonly = lua_toboolean(L, 2);
279         do {
280                 stat = (dataonly) ? fdatasync(fd) : fsync(fd);
281         } while (stat == -1 && errno == EINTR);
282         return nixio__pstatus(L, !stat);
283 #else
284         do {
285                 stat = fsync(fd);
286         } while (stat == -1 && errno == EINTR);
287         return nixio__pstatus(L, !stat);
288 #endif
289 }
290
291 static int nixio_file_lock(lua_State *L) {
292         int fd = nixio__checkfd(L, 1);
293         const char *flag = luaL_checkstring(L, 2);
294         off_t len = (off_t)nixio__optnumber(L, 3, 0);
295         int stat;
296
297         int cmd = 0;
298         if (!strcmp(flag, "lock")) {
299                 cmd = F_LOCK;
300         } else if (!strcmp(flag, "tlock")) {
301                 cmd = F_TLOCK;
302         } else if (!strcmp(flag, "ulock")) {
303                 cmd = F_ULOCK;
304         } else if (!strcmp(flag, "test")) {
305                 cmd = F_TEST;
306         } else {
307                 return luaL_argerror(L, 2,
308                                 "supported values: lock, tlock, ulock, test");
309         }
310
311         do {
312                 stat = lockf(fd, cmd, len);
313         } while (stat == -1 && errno == EINTR);
314
315         return nixio__pstatus(L, !stat);
316 }
317
318 static int nixio_file_close(lua_State *L) {
319         int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
320         luaL_argcheck(L, *fdp != -1, 1, "invalid file object");
321         int res;
322         do {
323                 res = close(*fdp);
324         } while (res == -1 && errno == EINTR);
325         *fdp = -1;
326         return nixio__pstatus(L, !res);
327 }
328
329 static int nixio_file__gc(lua_State *L) {
330         int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
331         int res;
332         if (*fdp > 2) {
333                 do {
334                         res = close(*fdp);
335                 } while (res == -1 && errno == EINTR);
336                 *fdp = -1;
337         }
338         return 0;
339 }
340
341 /**
342  * string representation
343  */
344 static int nixio_file__tostring(lua_State *L) {
345         lua_pushfstring(L, "nixio file %d", nixio__tofd(L, 1));
346         return 1;
347 }
348
349 /* method table */
350 static const luaL_reg M[] = {
351         {"write",               nixio_file_write},
352         {"read",                nixio_file_read},
353         {"tell",                nixio_file_tell},
354         {"seek",                nixio_file_seek},
355         {"stat",                nixio_file_stat},
356         {"sync",                nixio_file_sync},
357         {"lock",                nixio_file_lock},
358         {"close",               nixio_file_close},
359         {"__gc",                nixio_file__gc},
360         {"__tostring",  nixio_file__tostring},
361         {NULL,                  NULL}
362 };
363
364 /* module table */
365 static const luaL_reg R[] = {
366         {"dup",                 nixio_dup},
367         {"open",                nixio_open},
368         {"open_flags",  nixio_open_flags},
369         {"pipe",                nixio_pipe},
370         {NULL,                  NULL}
371 };
372
373 void nixio_open_file(lua_State *L) {
374         luaL_register(L, NULL, R);
375
376         luaL_newmetatable(L, NIXIO_FILE_META);
377         luaL_register(L, NULL, M);
378         lua_pushvalue(L, -1);
379         lua_setfield(L, -2, "__index");
380
381         int *uin  = lua_newuserdata(L, sizeof(int));
382         int *uout = lua_newuserdata(L, sizeof(int));
383         int *uerr = lua_newuserdata(L, sizeof(int));
384
385         if (!uin || !uout || !uerr) {
386                 luaL_error(L, "out of memory");
387         }
388
389         *uin  = STDIN_FILENO;
390         *uout = STDOUT_FILENO;
391         *uerr = STDERR_FILENO;
392
393         for (int i = -4; i < -1; i++) {
394                 lua_pushvalue(L, -4);
395                 lua_setmetatable(L, i);
396         }
397
398         lua_setfield(L, -5, "stderr");
399         lua_setfield(L, -4, "stdout");
400         lua_setfield(L, -3, "stdin");
401         lua_setfield(L, -2, "meta_file");
402 }