1 #define __USE_GNU
2 #include "tvheadend.h"
3 #include <fcntl.h>
4 #include <sys/types.h>          /* See NOTES */
5 #include <sys/socket.h>
6 #include <sys/stat.h>
7 #include <sys/resource.h>
8 #include <unistd.h>
9 #include <signal.h>
10 #include <pthread.h>
11 
12 #ifdef PLATFORM_LINUX
13 #include <sys/prctl.h>
14 #include <sys/syscall.h>
15 #endif
16 
17 #ifdef PLATFORM_FREEBSD
18 #include <pthread_np.h>
19 #endif
20 
21 /*
22  * filedescriptor routines
23  */
24 
25 int
tvh_open(const char * pathname,int flags,mode_t mode)26 tvh_open(const char *pathname, int flags, mode_t mode)
27 {
28   int fd;
29 
30   pthread_mutex_lock(&fork_lock);
31   fd = open(pathname, flags, mode);
32   if (fd != -1)
33     fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
34   pthread_mutex_unlock(&fork_lock);
35   return fd;
36 }
37 
38 int
tvh_socket(int domain,int type,int protocol)39 tvh_socket(int domain, int type, int protocol)
40 {
41   int fd;
42 
43   pthread_mutex_lock(&fork_lock);
44   fd = socket(domain, type, protocol);
45   if (fd != -1)
46     fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
47   pthread_mutex_unlock(&fork_lock);
48   return fd;
49 }
50 
51 int
tvh_pipe(int flags,th_pipe_t * p)52 tvh_pipe(int flags, th_pipe_t *p)
53 {
54   int fd[2], err;
55   pthread_mutex_lock(&fork_lock);
56   err = pipe(fd);
57   if (err != -1) {
58     fcntl(fd[0], F_SETFD, fcntl(fd[0], F_GETFD) | FD_CLOEXEC);
59     fcntl(fd[1], F_SETFD, fcntl(fd[1], F_GETFD) | FD_CLOEXEC);
60     fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | flags);
61     fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | flags);
62     p->rd = fd[0];
63     p->wr = fd[1];
64   }
65   pthread_mutex_unlock(&fork_lock);
66   return err;
67 }
68 
69 void
tvh_pipe_close(th_pipe_t * p)70 tvh_pipe_close(th_pipe_t *p)
71 {
72   close(p->rd);
73   close(p->wr);
74   p->rd = -1;
75   p->wr = -1;
76 }
77 
78 int
tvh_write(int fd,const void * buf,size_t len)79 tvh_write(int fd, const void *buf, size_t len)
80 {
81   int64_t limit = mclk() + sec2mono(25);
82   ssize_t c;
83 
84   while (len) {
85     c = write(fd, buf, len);
86     if (c < 0) {
87       if (ERRNO_AGAIN(errno)) {
88         if (mclk() > limit)
89           break;
90         tvh_safe_usleep(100);
91         continue;
92       }
93       break;
94     }
95     len -= c;
96     buf += c;
97   }
98 
99   return len ? 1 : 0;
100 }
101 
102 FILE *
tvh_fopen(const char * filename,const char * mode)103 tvh_fopen(const char *filename, const char *mode)
104 {
105   FILE *f;
106   int fd;
107   pthread_mutex_lock(&fork_lock);
108   f = fopen(filename, mode);
109   if (f) {
110     fd = fileno(f);
111     fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
112   }
113   pthread_mutex_unlock(&fork_lock);
114   return f;
115 }
116 
117 /*
118  * thread routines
119  */
120 
doquit(int sig)121 static void doquit(int sig)
122 {
123 }
124 
125 struct
126 thread_state {
127   void *(*run)(void*);
128   void *arg;
129   char name[17];
130 };
131 
132 static void *
thread_wrapper(void * p)133 thread_wrapper ( void *p )
134 {
135   struct thread_state *ts = p;
136   sigset_t set;
137 
138 #if defined(PLATFORM_LINUX)
139   /* Set name */
140   prctl(PR_SET_NAME, ts->name);
141 #elif defined(PLATFORM_FREEBSD)
142   /* Set name of thread */
143   pthread_set_name_np(pthread_self(), ts->name);
144 #elif defined(PLATFORM_DARWIN)
145   pthread_setname_np(ts->name);
146 #endif
147 
148   sigemptyset(&set);
149   sigaddset(&set, SIGTERM);
150   sigaddset(&set, SIGQUIT);
151   pthread_sigmask(SIG_UNBLOCK, &set, NULL);
152 
153   signal(SIGTERM, doexit);
154   signal(SIGQUIT, doquit);
155 
156   /* Run */
157   tvhtrace(LS_THREAD, "created thread %ld [%s / %p(%p)]",
158            (long)pthread_self(), ts->name, ts->run, ts->arg);
159   void *r = ts->run(ts->arg);
160   free(ts);
161 
162   return r;
163 }
164 
165 int
tvhthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg,const char * name)166 tvhthread_create
167   (pthread_t *thread, const pthread_attr_t *attr,
168    void *(*start_routine) (void *), void *arg, const char *name)
169 {
170   int r;
171   struct thread_state *ts = calloc(1, sizeof(struct thread_state));
172   pthread_attr_t _attr;
173   if (attr == NULL) {
174     pthread_attr_init(&_attr);
175     pthread_attr_setstacksize(&_attr, 2*1024*1024);
176     attr = &_attr;
177   }
178   strlcpy(ts->name, "tvh:", 5);
179   strlcpy(ts->name+4, name, sizeof(ts->name)-4);
180   ts->run  = start_routine;
181   ts->arg  = arg;
182   r = pthread_create(thread, attr, thread_wrapper, ts);
183   return r;
184 }
185 
186 /* linux style: -19 .. 20 */
187 int
tvhthread_renice(int value)188 tvhthread_renice(int value)
189 {
190   int ret = 0;
191 #ifdef SYS_gettid
192   pid_t tid;
193   tid = syscall(SYS_gettid);
194   ret = setpriority(PRIO_PROCESS, tid, value);
195 #elif ENABLE_ANDROID
196   pid_t tid;
197   tid = gettid();
198   ret = setpriority(PRIO_PROCESS, tid, value);
199 #elif defined(PLATFORM_DARWIN)
200   /* Currently not possible */
201 #elif defined(PLATFORM_FREEBSD)
202   /* Currently not possible */
203 #else
204 #warning "Implement renice for your platform!"
205 #endif
206   return ret;
207 }
208 
209 int
tvh_mutex_timedlock(pthread_mutex_t * mutex,int64_t usec)210 tvh_mutex_timedlock
211   ( pthread_mutex_t *mutex, int64_t usec )
212 {
213   int64_t finish = getfastmonoclock() + usec;
214   int retcode;
215 
216   while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
217     if (getfastmonoclock() >= finish)
218       return ETIMEDOUT;
219 
220     tvh_safe_usleep(10000);
221   }
222 
223   return retcode;
224 }
225 
226 /*
227  * thread condition variables - monotonic clocks
228  */
229 
230 int
tvh_cond_init(tvh_cond_t * cond)231 tvh_cond_init
232   ( tvh_cond_t *cond )
233 {
234   int r;
235 
236   pthread_condattr_t attr;
237   pthread_condattr_init(&attr);
238 #if defined(PLATFORM_DARWIN)
239   /*
240    * pthread_condattr_setclock() not supported on platform Darwin.
241    * We use pthread_cond_timedwait_relative_np() which doesn't
242    * need it.
243    */
244    r = 0;
245 #else
246   r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
247   if (r) {
248     fprintf(stderr, "Unable to set monotonic clocks for conditions! (%d)", r);
249     abort();
250   }
251 #endif
252   return pthread_cond_init(&cond->cond, &attr);
253 }
254 
255 int
tvh_cond_destroy(tvh_cond_t * cond)256 tvh_cond_destroy
257   ( tvh_cond_t *cond )
258 {
259   return pthread_cond_destroy(&cond->cond);
260 }
261 
262 int
tvh_cond_signal(tvh_cond_t * cond,int broadcast)263 tvh_cond_signal
264   ( tvh_cond_t *cond, int broadcast )
265 {
266   if (broadcast)
267     return pthread_cond_broadcast(&cond->cond);
268   else
269     return pthread_cond_signal(&cond->cond);
270 }
271 
272 int
tvh_cond_wait(tvh_cond_t * cond,pthread_mutex_t * mutex)273 tvh_cond_wait
274   ( tvh_cond_t *cond, pthread_mutex_t *mutex)
275 {
276   return pthread_cond_wait(&cond->cond, mutex);
277 }
278 
279 int
tvh_cond_timedwait(tvh_cond_t * cond,pthread_mutex_t * mutex,int64_t monoclock)280 tvh_cond_timedwait
281   ( tvh_cond_t *cond, pthread_mutex_t *mutex, int64_t monoclock )
282 {
283 #if defined(PLATFORM_DARWIN)
284   /* Use a relative timedwait implementation */
285   int64_t now = getmonoclock();
286   int64_t relative = monoclock - now;
287 
288   struct timespec ts;
289   ts.tv_sec  = relative / MONOCLOCK_RESOLUTION;
290   ts.tv_nsec = (relative % MONOCLOCK_RESOLUTION) *
291                (1000000000ULL/MONOCLOCK_RESOLUTION);
292 
293   return pthread_cond_timedwait_relative_np(&cond->cond, mutex, &ts);
294 #else
295   struct timespec ts;
296   ts.tv_sec = monoclock / MONOCLOCK_RESOLUTION;
297   ts.tv_nsec = (monoclock % MONOCLOCK_RESOLUTION) *
298                (1000000000ULL/MONOCLOCK_RESOLUTION);
299   return pthread_cond_timedwait(&cond->cond, mutex, &ts);
300 #endif
301 }
302 
303 /*
304  * clocks
305  */
306 
307 void
tvh_safe_usleep(int64_t us)308 tvh_safe_usleep(int64_t us)
309 {
310   int64_t r;
311   if (us <= 0)
312     return;
313   do {
314     r = tvh_usleep(us);
315     if (r < 0) {
316       if (ERRNO_AGAIN(-r))
317         continue;
318       break;
319     }
320     us = r;
321   } while (r > 0);
322 }
323 
324 int64_t
tvh_usleep(int64_t us)325 tvh_usleep(int64_t us)
326 {
327 #if defined(PLATFORM_DARWIN) || defined(PLATFORM_FREEBSD)
328   return usleep(us);
329 #else
330   struct timespec ts;
331   int64_t val;
332   int r;
333   if (us <= 0)
334     return 0;
335   ts.tv_sec = us / 1000000LL;
336   ts.tv_nsec = (us % 1000000LL) * 1000LL;
337   r = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
338   val = (ts.tv_sec * 1000000LL) + ((ts.tv_nsec + 500LL) / 1000LL);
339   if (ERRNO_AGAIN(r))
340     return val;
341   return r ? -r : 0;
342 #endif
343 }
344 
345 int64_t
tvh_usleep_abs(int64_t us)346 tvh_usleep_abs(int64_t us)
347 {
348 #if defined(PLATFORM_DARWIN) || defined(PLATFORM_FREEBSD)
349   /* Convert to relative wait */
350   int64_t now = getmonoclock();
351   int64_t relative = us - now;
352   return tvh_usleep(relative);
353 #else
354   struct timespec ts;
355   int64_t val;
356   int r;
357   if (us <= 0)
358     return 0;
359   ts.tv_sec = us / 1000000LL;
360   ts.tv_nsec = (us % 1000000LL) * 1000LL;
361   r = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, &ts);
362   val = (ts.tv_sec * 1000000LL) + ((ts.tv_nsec + 500LL) / 1000LL);
363   if (ERRNO_AGAIN(r))
364     return val;
365   return r ? -r : 0;
366 #endif
367 }
368 
369 /*
370  * qsort
371  */
372 
373 #if ! ENABLE_QSORT_R
374 /*
375  * qsort_r wrapper for pre GLIBC 2.8
376  */
377 
378 static __thread struct {
379   int (*cmp) ( const void *a, const void *b, void *p );
380   void *aux;
381 } qsort_r_data;
382 
383 static int
qsort_r_wrap(const void * a,const void * b)384 qsort_r_wrap ( const void *a, const void *b )
385 {
386   return qsort_r_data.cmp(a, b, qsort_r_data.aux);
387 }
388 
389 void
qsort_r(void * base,size_t nmemb,size_t size,int (* cmp)(const void *,const void *,void *),void * aux)390 qsort_r(void *base, size_t nmemb, size_t size,
391        int (*cmp)(const void *, const void *, void *), void *aux)
392 {
393   qsort_r_data.cmp = cmp;
394   qsort_r_data.aux = aux;
395   qsort(base, nmemb, size, qsort_r_wrap);
396 }
397 #endif /* ENABLE_QSORT_R */
398 
399 
400 #if defined(PLATFORM_FREEBSD) || defined(PLATFORM_DARWIN)
401 struct tvh_qsort_data {
402     void *arg;
403     int (*compar)(const void *, const void *, void *);
404 };
405 
406 
407 static int
tvh_qsort_swap(void * arg,const void * a,const void * b)408 tvh_qsort_swap(void *arg, const void *a, const void *b)
409 {
410     struct tvh_qsort_data *data = arg;
411     return data->compar(a, b, data->arg);
412 }
413 #endif /* PLATFORM_FREEBSD || PLATFORM_DARWIN */
414 
415 
416 void
tvh_qsort_r(void * base,size_t nmemb,size_t size,int (* compar)(const void *,const void *,void *),void * arg)417 tvh_qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)
418 {
419 #if defined(PLATFORM_FREEBSD) || defined(PLATFORM_DARWIN)
420     struct tvh_qsort_data swap_arg = {arg, compar};
421     qsort_r(base, nmemb, size, &swap_arg, tvh_qsort_swap);
422 #else
423     qsort_r(base, nmemb, size, compar, arg);
424 #endif
425 }
426