1 //
2 /**************************************************************************************************
3 * IOWOW library
4 *
5 * MIT License
6 *
7 * Copyright (c) 2012-2022 Softmotions Ltd <info@softmotions.com>
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 all
17 * 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 THE
25 * SOFTWARE.
26 *************************************************************************************************/
27
28 #include "iwcfg.h"
29 #include "log/iwlog.h"
30 #include "platform/iwp.h"
31 #include "utils/iwutils.h"
32
33 #include <time.h>
34 #include <math.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <ftw.h>
40 #include <pthread.h>
41
42 #if defined(__linux__)
43 #include <sys/prctl.h>
44 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
45 #include <pthread_np.h>
46 #include <sys/sysctl.h>
47 #endif
48
49 #ifdef __APPLE__
50 #include <libproc.h>
51
52 #define st_atim st_atimespec
53 #define st_ctim st_ctimespec
54 #define st_mtim st_mtimespec
55 #endif
56
57 #define _IW_TIMESPEC2MS(IW_ts) (((IW_ts).tv_sec * 1000ULL) + lround((IW_ts).tv_nsec / 1.0e6))
58
iwp_clock_get_time(int clock_id,struct timespec * t)59 IW_EXPORT iwrc iwp_clock_get_time(int clock_id, struct timespec *t) {
60 #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
61 struct timeval now;
62 int rci = gettimeofday(&now, NULL);
63 if (rci) {
64 return iwrc_set_errno(IW_ERROR_ERRNO, errno);
65 }
66 t->tv_sec = now.tv_sec;
67 t->tv_nsec = now.tv_usec * 1000ULL;
68 #else
69 int rci = clock_gettime(clock_id, t);
70 if (rci) {
71 return iwrc_set_errno(IW_ERROR_ERRNO, errno);
72 }
73 #endif
74 return 0;
75 }
76
iwp_current_time_ms(uint64_t * time,bool monotonic)77 iwrc iwp_current_time_ms(uint64_t *time, bool monotonic) {
78 struct timespec spec;
79 #ifdef IW_HAVE_CLOCK_MONOTONIC
80 iwrc rc = iwp_clock_get_time(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &spec);
81 #else
82 iwrc rc = iwp_clock_get_time(CLOCK_REALTIME, &spec);
83 #endif
84 if (rc) {
85 *time = 0;
86 return rc;
87 }
88 *time = _IW_TIMESPEC2MS(spec);
89 return 0;
90 }
91
_iwp_fstat(const char * path,HANDLE fd,IWP_FILE_STAT * fs)92 static iwrc _iwp_fstat(const char *path, HANDLE fd, IWP_FILE_STAT *fs) {
93 assert(fs);
94 iwrc rc = 0;
95 struct stat st = { 0 };
96 memset(fs, 0, sizeof(*fs));
97 if (path) {
98 if (stat(path, &st)) {
99 return (errno == ENOENT) ? IW_ERROR_NOT_EXISTS : iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
100 }
101 } else {
102 if (fstat(fd, &st)) {
103 return (errno == ENOENT) ? IW_ERROR_NOT_EXISTS : iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
104 }
105 }
106 fs->atime = _IW_TIMESPEC2MS(st.st_atim);
107 fs->mtime = _IW_TIMESPEC2MS(st.st_mtim);
108 fs->ctime = _IW_TIMESPEC2MS(st.st_ctim);
109 fs->size = (uint64_t) st.st_size;
110
111 if (S_ISREG(st.st_mode)) {
112 fs->ftype = IWP_TYPE_FILE;
113 } else if (S_ISDIR(st.st_mode)) {
114 fs->ftype = IWP_TYPE_DIR;
115 } else if (S_ISLNK(st.st_mode)) {
116 fs->ftype = IWP_LINK;
117 } else {
118 fs->ftype = IWP_OTHER;
119 }
120 return rc;
121 }
122
iwp_fstat(const char * path,IWP_FILE_STAT * fs)123 iwrc iwp_fstat(const char *path, IWP_FILE_STAT *fs) {
124 return _iwp_fstat(path, 0, fs);
125 }
126
iwp_fstath(HANDLE fh,IWP_FILE_STAT * fs)127 iwrc iwp_fstath(HANDLE fh, IWP_FILE_STAT *fs) {
128 return _iwp_fstat(0, fh, fs);
129 }
130
iwp_flock(HANDLE fh,iwp_lockmode lmode)131 iwrc iwp_flock(HANDLE fh, iwp_lockmode lmode) {
132 if (INVALIDHANDLE(fh)) {
133 return IW_ERROR_INVALID_HANDLE;
134 }
135 if (lmode == IWP_NOLOCK) {
136 return 0;
137 }
138 struct flock lock = { .l_type = (lmode & IWP_WLOCK) ? F_WRLCK : F_RDLCK, .l_whence = SEEK_SET };
139 while (fcntl(fh, (lmode & IWP_NBLOCK) ? F_SETLK : F_SETLKW, &lock) == -1) {
140 if (errno != EINTR) {
141 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
142 }
143 }
144 return 0;
145 }
146
iwp_unlock(HANDLE fh)147 iwrc iwp_unlock(HANDLE fh) {
148 if (INVALIDHANDLE(fh)) {
149 return IW_ERROR_INVALID_HANDLE;
150 }
151 struct flock lock = { .l_type = F_UNLCK, .l_whence = SEEK_SET };
152 while (fcntl(fh, F_SETLKW, &lock) == -1) {
153 if (errno != EINTR) {
154 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
155 }
156 }
157 return 0;
158 }
159
iwp_closefh(HANDLE fh)160 iwrc iwp_closefh(HANDLE fh) {
161 if (INVALIDHANDLE(fh)) {
162 return 0;
163 }
164 if (close(fh) == -1) {
165 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
166 }
167 return 0;
168 }
169
iwp_pread(HANDLE fh,off_t off,void * buf,size_t siz,size_t * sp)170 iwrc iwp_pread(HANDLE fh, off_t off, void *buf, size_t siz, size_t *sp) {
171 ssize_t rci;
172
173 again:
174 rci = pread(fh, buf, siz, off);
175 if (rci < 0) {
176 *sp = 0;
177 if (errno == EINTR) {
178 goto again;
179 } else if (errno == EWOULDBLOCK || errno == IW_ERROR_AGAIN) {
180 return IW_ERROR_AGAIN;
181 }
182 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
183 }
184 *sp = rci;
185 return 0;
186 }
187
iwp_read(HANDLE fh,void * buf,size_t count,size_t * sp)188 iwrc iwp_read(HANDLE fh, void *buf, size_t count, size_t *sp) {
189 ssize_t rs;
190
191 again:
192 rs = read(fh, buf, count);
193 if (rs < 0) {
194 *sp = 0;
195 if (errno == EINTR) {
196 goto again;
197 } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
198 return IW_ERROR_AGAIN;
199 }
200 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
201 }
202 *sp = rs;
203 return 0;
204 }
205
iwp_pwrite(HANDLE fh,off_t off,const void * buf,size_t siz,size_t * sp)206 iwrc iwp_pwrite(HANDLE fh, off_t off, const void *buf, size_t siz, size_t *sp) {
207 ssize_t ws;
208
209 again:
210 ws = pwrite(fh, buf, siz, off);
211 if (ws < 0) {
212 *sp = 0;
213 if (errno == EINTR) {
214 goto again;
215 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
216 return IW_ERROR_AGAIN;
217 }
218 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
219 }
220 *sp = ws;
221 return 0;
222 }
223
iwp_write(HANDLE fh,const void * buf,size_t size)224 iwrc iwp_write(HANDLE fh, const void *buf, size_t size) {
225 const char *rp = buf;
226 do {
227 ssize_t wb = write(fh, rp, size);
228 if (wb < 0) {
229 if (errno == EINTR) {
230 continue;
231 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
232 return IW_ERROR_AGAIN;
233 }
234 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
235 } else {
236 rp += wb;
237 size -= wb;
238 }
239 } while (size > 0);
240 return 0;
241 }
242
iwp_lseek(HANDLE fh,off_t offset,iwp_seek_origin origin,off_t * pos)243 iwrc iwp_lseek(HANDLE fh, off_t offset, iwp_seek_origin origin, off_t *pos) {
244 if (pos) {
245 *pos = 0;
246 }
247 if (INVALIDHANDLE(fh)) {
248 return IW_ERROR_INVALID_HANDLE;
249 }
250 int whence = SEEK_SET;
251 if (origin == IWP_SEEK_CUR) {
252 whence = SEEK_CUR;
253 } else if (origin == IWP_SEEK_END) {
254 whence = SEEK_END;
255 }
256 off_t off = lseek(fh, offset, whence);
257 if (off < 0) {
258 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
259 } else {
260 if (pos) {
261 *pos = off;
262 }
263 return 0;
264 }
265 }
266
iwp_page_size(void)267 size_t iwp_page_size(void) {
268 static long int _iwp_pagesize = 0;
269 if (!_iwp_pagesize) {
270 _iwp_pagesize = sysconf(_SC_PAGESIZE);
271 }
272 return (size_t) _iwp_pagesize;
273 }
274
iwp_alloc_unit(void)275 size_t iwp_alloc_unit(void) {
276 return iwp_page_size();
277 }
278
iwp_ftruncate(HANDLE fh,off_t len)279 iwrc iwp_ftruncate(HANDLE fh, off_t len) {
280 int rci = ftruncate(fh, len);
281 return !rci ? 0 : iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
282 }
283
iwp_fallocate(HANDLE fh,off_t len)284 iwrc iwp_fallocate(HANDLE fh, off_t len) {
285 #if defined(__APPLE__)
286 fstore_t fstore = {
287 .fst_flags = F_ALLOCATECONTIG,
288 .fst_posmode = F_PEOFPOSMODE,
289 .fst_length = len
290 };
291 fcntl(fh, F_PREALLOCATE, &fstore);
292 #endif
293 int rci = ftruncate(fh, len);
294 return !rci ? 0 : iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
295 }
296
iwp_sleep(uint64_t ms)297 iwrc iwp_sleep(uint64_t ms) {
298 iwrc rc = 0;
299 struct timespec req;
300 req.tv_sec = ms / 1000UL;
301 req.tv_nsec = (ms % 1000UL) * 1000UL * 1000UL;
302 again:
303 if (nanosleep(&req, NULL) == -1) {
304 if (errno == EINTR) {
305 goto again;
306 }
307 rc = iwrc_set_errno(IW_ERROR_THREADING_ERRNO, errno);
308 }
309 return rc;
310 }
311
_rmfile(const char * pathname,const struct stat * sbuf,int type,struct FTW * ftwb)312 static int _rmfile(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) {
313 if (remove(pathname) < 0) {
314 perror(pathname);
315 return -1;
316 }
317 return 0;
318 }
319
iwp_removedir(const char * path)320 iwrc iwp_removedir(const char *path) {
321 if (nftw(path, _rmfile, 10, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) < 0) {
322 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
323 }
324 return 0;
325 }
326
iwp_exec_path(char * opath,size_t opath_maxlen)327 iwrc iwp_exec_path(char *opath, size_t opath_maxlen) {
328 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
329 const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
330 if (sysctl(mib, 4, opath, &opath_maxlen, 0, 0) < 0) {
331 return iwrc_set_errno(IW_ERROR_ERRNO, errno);
332 }
333 return 0;
334 #elif defined(__linux__)
335 char *path = "/proc/self/exe";
336 ssize_t ret = readlink(path, opath, opath_maxlen);
337 if (ret == -1) {
338 return iwrc_set_errno(IW_ERROR_ERRNO, errno);
339 } else if (ret < opath_maxlen) {
340 opath[ret] = '\0';
341 } else if (opath_maxlen > 0) {
342 opath[opath_maxlen - 1] = '\0';
343 }
344 return 0;
345 #elif defined(__APPLE__)
346 pid_t pid = getpid();
347 int ret = proc_pidpath(pid, opath, opath_maxlen);
348 if (ret < 0) {
349 return iwrc_set_errno(IW_ERROR_ERRNO, errno);
350 }
351 #else
352 // TODO:
353 return IW_ERROR_NOT_IMPLEMENTED;
354 #endif
355 }
356
iwp_num_cpu_cores(void)357 uint16_t iwp_num_cpu_cores(void) {
358 long res = sysconf(_SC_NPROCESSORS_ONLN);
359 return (uint16_t) (res > 0 ? res : 1);
360 }
361
iwp_fsync(HANDLE fh)362 iwrc iwp_fsync(HANDLE fh) {
363 int rci = fsync(fh);
364 return rci ? iwrc_set_errno(IW_ERROR_IO_ERRNO, errno) : 0;
365 }
366
iwp_fdatasync(HANDLE fh)367 iwrc iwp_fdatasync(HANDLE fh) {
368 #ifdef __APPLE__
369 if (fcntl(fh, F_FULLFSYNC) == -1) {
370 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
371 }
372 #else
373 if (fdatasync(fh) == -1) {
374 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
375 }
376 #endif
377 return 0;
378 }
379
iwp_tmpdir(char * out,size_t len)380 size_t iwp_tmpdir(char *out, size_t len) {
381 const char *tdir;
382 #ifdef IW_TMPDIR
383 tdir = IW_TMPDIR;
384 #else
385 tdir = getenv("TMPDIR");
386 if (!tdir) {
387 #ifdef P_tmpdir
388 tdir = P_tmpdir;
389 #else
390 tdir = "/tmp";
391 #endif
392 }
393 #endif
394 size_t tlen = strlen(tdir);
395 size_t nw = MIN(len, tlen);
396 memcpy(out, tdir, nw);
397 return nw;
398 }
399
iwp_set_current_thread_name(const char * name)400 void iwp_set_current_thread_name(const char *name) {
401 #if (defined(__APPLE__) && defined(__MACH__)) || defined(__linux__)
402 // On linux and OS X the name may not be longer than 16 bytes, including
403 // the null terminator. Truncate the name to 15 characters.
404 char buf[16];
405 strncpy(buf, name, sizeof(buf) - 1);
406 buf[sizeof(buf) - 1] = '\0';
407 name = buf;
408 #endif
409
410 #if defined(__linux__)
411 prctl(PR_SET_NAME, name);
412 #elif defined(__NetBSD__)
413 rv = pthread_setname_np(pthread_self(), "%s", (void*) name);
414 #elif defined(__APPLE__)
415 pthread_setname_np(name);
416 #else
417 pthread_setname_np(pthread_self(), name);
418 #endif
419 }
420
_iwp_init_impl(void)421 static iwrc _iwp_init_impl(void) {
422 iwp_page_size(); // init statics
423 return 0;
424 }
425