• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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