1/*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2014-2018 Paul Sokolovsky
7 * Copyright (c) 2014-2018 Damien P. George
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 */
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h>
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <dirent.h>
36#ifdef _MSC_VER
37#include <direct.h> // For mkdir
38#endif
39#include "py/mpconfig.h"
40
41#include "py/runtime.h"
42#include "py/objtuple.h"
43#include "py/mphal.h"
44#include "py/mpthread.h"
45#include "extmod/vfs.h"
46#include "extmod/misc.h"
47
48#ifdef __ANDROID__
49#define USE_STATFS 1
50#endif
51
52STATIC mp_obj_t mod_os_stat(mp_obj_t path_in) {
53 struct stat sb;
54 const char *path = mp_obj_str_get_str(path_in);
55
56 int res;
57 MP_HAL_RETRY_SYSCALL(res, stat(path, &sb), mp_raise_OSError(err));
58
59 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
60 t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode);
61 t->items[1] = mp_obj_new_int_from_uint(sb.st_ino);
62 t->items[2] = mp_obj_new_int_from_uint(sb.st_dev);
63 t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink);
64 t->items[4] = mp_obj_new_int_from_uint(sb.st_uid);
65 t->items[5] = mp_obj_new_int_from_uint(sb.st_gid);
66 t->items[6] = mp_obj_new_int_from_uint(sb.st_size);
67 t->items[7] = mp_obj_new_int_from_uint(sb.st_atime);
68 t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime);
69 t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime);
70 return MP_OBJ_FROM_PTR(t);
71}
72STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_stat_obj, mod_os_stat);
73
74#if MICROPY_PY_OS_STATVFS
75
76#if USE_STATFS
77#include <sys/vfs.h>
78#define STRUCT_STATVFS struct statfs
79#define STATVFS statfs
80#define F_FAVAIL sb.f_ffree
81#define F_NAMEMAX sb.f_namelen
82#define F_FLAG sb.f_flags
83#else
84#include <sys/statvfs.h>
85#define STRUCT_STATVFS struct statvfs
86#define STATVFS statvfs
87#define F_FAVAIL sb.f_favail
88#define F_NAMEMAX sb.f_namemax
89#define F_FLAG sb.f_flag
90#endif
91
92STATIC mp_obj_t mod_os_statvfs(mp_obj_t path_in) {
93 STRUCT_STATVFS sb;
94 const char *path = mp_obj_str_get_str(path_in);
95
96 int res;
97 MP_HAL_RETRY_SYSCALL(res, STATVFS(path, &sb), mp_raise_OSError(err));
98
99 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
100 t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
101 t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize);
102 t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks);
103 t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree);
104 t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail);
105 t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files);
106 t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree);
107 t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL);
108 t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG);
109 t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX);
110 return MP_OBJ_FROM_PTR(t);
111}
112STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_statvfs_obj, mod_os_statvfs);
113#endif
114
115STATIC mp_obj_t mod_os_remove(mp_obj_t path_in) {
116 const char *path = mp_obj_str_get_str(path_in);
117
118 // Note that POSIX requires remove() to be able to delete a directory
119 // too (act as rmdir()). This is POSIX extenstion to ANSI C semantics
120 // of that function. But Python remove() follows ANSI C, and explicitly
121 // required to raise exception on attempt to remove a directory. Thus,
122 // call POSIX unlink() here.
123 MP_THREAD_GIL_EXIT();
124 int r = unlink(path);
125 MP_THREAD_GIL_ENTER();
126
127 RAISE_ERRNO(r, errno);
128
129 return mp_const_none;
130}
131STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_remove_obj, mod_os_remove);
132
133STATIC mp_obj_t mod_os_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) {
134 const char *old_path = mp_obj_str_get_str(old_path_in);
135 const char *new_path = mp_obj_str_get_str(new_path_in);
136
137 MP_THREAD_GIL_EXIT();
138 int r = rename(old_path, new_path);
139 MP_THREAD_GIL_ENTER();
140
141 RAISE_ERRNO(r, errno);
142
143 return mp_const_none;
144}
145STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_os_rename_obj, mod_os_rename);
146
147STATIC mp_obj_t mod_os_rmdir(mp_obj_t path_in) {
148 const char *path = mp_obj_str_get_str(path_in);
149
150 MP_THREAD_GIL_EXIT();
151 int r = rmdir(path);
152 MP_THREAD_GIL_ENTER();
153
154 RAISE_ERRNO(r, errno);
155
156 return mp_const_none;
157}
158STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_rmdir_obj, mod_os_rmdir);
159
160STATIC mp_obj_t mod_os_system(mp_obj_t cmd_in) {
161 const char *cmd = mp_obj_str_get_str(cmd_in);
162
163 MP_THREAD_GIL_EXIT();
164 int r = system(cmd);
165 MP_THREAD_GIL_ENTER();
166
167 RAISE_ERRNO(r, errno);
168
169 return MP_OBJ_NEW_SMALL_INT(r);
170}
171MP_DEFINE_CONST_FUN_OBJ_1(mod_os_system_obj, mod_os_system);
172
173STATIC mp_obj_t mod_os_getenv(mp_obj_t var_in) {
174 const char *s = getenv(mp_obj_str_get_str(var_in));
175 if (s == NULL) {
176 return mp_const_none;
177 }
178 return mp_obj_new_str(s, strlen(s));
179}
180MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_obj, mod_os_getenv);
181
182STATIC mp_obj_t mod_os_putenv(mp_obj_t key_in, mp_obj_t value_in) {
183 const char *key = mp_obj_str_get_str(key_in);
184 const char *value = mp_obj_str_get_str(value_in);
185 int ret;
186
187 #if _WIN32
188 ret = _putenv_s(key, value);
189 #else
190 ret = setenv(key, value, 1);
191 #endif
192
193 if (ret == -1) {
194 mp_raise_OSError(errno);
195 }
196 return mp_const_none;
197}
198MP_DEFINE_CONST_FUN_OBJ_2(mod_os_putenv_obj, mod_os_putenv);
199
200STATIC mp_obj_t mod_os_unsetenv(mp_obj_t key_in) {
201 const char *key = mp_obj_str_get_str(key_in);
202 int ret;
203
204 #if _WIN32
205 ret = _putenv_s(key, "");
206 #else
207 ret = unsetenv(key);
208 #endif
209
210 if (ret == -1) {
211 mp_raise_OSError(errno);
212 }
213 return mp_const_none;
214}
215MP_DEFINE_CONST_FUN_OBJ_1(mod_os_unsetenv_obj, mod_os_unsetenv);
216
217STATIC mp_obj_t mod_os_mkdir(mp_obj_t path_in) {
218 // TODO: Accept mode param
219 const char *path = mp_obj_str_get_str(path_in);
220 MP_THREAD_GIL_EXIT();
221 #ifdef _WIN32
222 int r = mkdir(path);
223 #else
224 int r = mkdir(path, 0777);
225 #endif
226 MP_THREAD_GIL_ENTER();
227 RAISE_ERRNO(r, errno);
228 return mp_const_none;
229}
230STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_mkdir_obj, mod_os_mkdir);
231
232typedef struct _mp_obj_listdir_t {
233 mp_obj_base_t base;
234 mp_fun_1_t iternext;
235 DIR *dir;
236} mp_obj_listdir_t;
237
238STATIC mp_obj_t listdir_next(mp_obj_t self_in) {
239 mp_obj_listdir_t *self = MP_OBJ_TO_PTR(self_in);
240
241 if (self->dir == NULL) {
242 goto done;
243 }
244 MP_THREAD_GIL_EXIT();
245 struct dirent *dirent = readdir(self->dir);
246 if (dirent == NULL) {
247 closedir(self->dir);
248 MP_THREAD_GIL_ENTER();
249 self->dir = NULL;
250 done:
251 return MP_OBJ_STOP_ITERATION;
252 }
253 MP_THREAD_GIL_ENTER();
254
255 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
256 t->items[0] = mp_obj_new_str(dirent->d_name, strlen(dirent->d_name));
257
258 #ifdef _DIRENT_HAVE_D_TYPE
259 #ifdef DTTOIF
260 t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type));
261 #else
262 if (dirent->d_type == DT_DIR) {
263 t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
264 } else if (dirent->d_type == DT_REG) {
265 t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
266 } else {
267 t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type);
268 }
269 #endif
270 #else
271 // DT_UNKNOWN should have 0 value on any reasonable system
272 t->items[1] = MP_OBJ_NEW_SMALL_INT(0);
273 #endif
274
275 #ifdef _DIRENT_HAVE_D_INO
276 t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino);
277 #else
278 t->items[2] = MP_OBJ_NEW_SMALL_INT(0);
279 #endif
280 return MP_OBJ_FROM_PTR(t);
281}
282
283STATIC mp_obj_t mod_os_ilistdir(size_t n_args, const mp_obj_t *args) {
284 const char *path = ".";
285 if (n_args > 0) {
286 path = mp_obj_str_get_str(args[0]);
287 }
288 mp_obj_listdir_t *o = m_new_obj(mp_obj_listdir_t);
289 o->base.type = &mp_type_polymorph_iter;
290 MP_THREAD_GIL_EXIT();
291 o->dir = opendir(path);
292 MP_THREAD_GIL_ENTER();
293 o->iternext = listdir_next;
294 return MP_OBJ_FROM_PTR(o);
295}
296STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_ilistdir_obj, 0, 1, mod_os_ilistdir);
297
298STATIC mp_obj_t mod_os_errno(size_t n_args, const mp_obj_t *args) {
299 if (n_args == 0) {
300 return MP_OBJ_NEW_SMALL_INT(errno);
301 }
302
303 errno = mp_obj_get_int(args[0]);
304 return mp_const_none;
305}
306MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_errno_obj, 0, 1, mod_os_errno);
307
308STATIC const mp_rom_map_elem_t mp_module_os_globals_table[] = {
309 { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) },
310 { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mod_os_errno_obj) },
311 { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_os_stat_obj) },
312 #if MICROPY_PY_OS_STATVFS
313 { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mod_os_statvfs_obj) },
314 #endif
315 { MP_ROM_QSTR(MP_QSTR_system), MP_ROM_PTR(&mod_os_system_obj) },
316 { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mod_os_remove_obj) },
317 { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mod_os_rename_obj) },
318 { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mod_os_rmdir_obj) },
319 { MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mod_os_getenv_obj) },
320 { MP_ROM_QSTR(MP_QSTR_putenv), MP_ROM_PTR(&mod_os_putenv_obj) },
321 { MP_ROM_QSTR(MP_QSTR_unsetenv), MP_ROM_PTR(&mod_os_unsetenv_obj) },
322 { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_os_mkdir_obj) },
323 { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mod_os_ilistdir_obj) },
324 #if MICROPY_PY_OS_DUPTERM
325 { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) },
326 #endif
327};
328
329STATIC MP_DEFINE_CONST_DICT(mp_module_os_globals, mp_module_os_globals_table);
330
331const mp_obj_module_t mp_module_os = {
332 .base = { &mp_type_module },
333 .globals = (mp_obj_dict_t *)&mp_module_os_globals,
334};
335