1 /*
2 
3     MPDM - Minimum Profit Data Manager
4     mpdm_t.c - Threads, mutexes, semaphores and other stuff
5 
6     ttcdt <dev@triptico.com> et al.
7 
8     This software is released into the public domain.
9     NO WARRANTY. See file LICENSE for details.
10 
11 */
12 
13 #include "config.h"
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <locale.h>
19 #include <time.h>
20 
21 #ifdef CONFOPT_WIN32
22 #include <windows.h>
23 #endif
24 
25 #ifdef CONFOPT_PTHREADS
26 #include <pthread.h>
27 #endif
28 
29 #ifdef CONFOPT_POSIXSEMS
30 #include <semaphore.h>
31 #endif
32 
33 #ifdef CONFOPT_GETTIMEOFDAY
34 #include <sys/time.h>
35 #endif
36 
37 #ifdef CONFOPT_ZLIB
38 #include <zlib.h>
39 #endif
40 
41 #include "mpdm.h"
42 
43 
44 /** code **/
45 
46 /**
47  * mpdm_sleep - Sleeps a number of milliseconds.
48  * @msecs: the milliseconds to sleep
49  *
50  * Sleeps a number of milliseconds.
51  * [Threading]
52  */
mpdm_sleep(int msecs)53 void mpdm_sleep(int msecs)
54 {
55 #ifdef CONFOPT_WIN32
56 
57     Sleep(msecs);
58 
59 #endif
60 
61 #ifdef CONFOPT_NANOSLEEP
62     struct timespec ts;
63 
64     ts.tv_sec = msecs / 1000;
65     ts.tv_nsec = (msecs % 1000) * 1000000;
66 
67     nanosleep(&ts, NULL);
68 #endif
69 }
70 
71 
mpdm_time(void)72 double mpdm_time(void)
73 {
74     double r = 0.0;
75 
76 #ifdef CONFOPT_GETTIMEOFDAY
77 
78     struct timeval tv;
79 
80     gettimeofday(&tv, NULL);
81 
82     r = ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0);
83 
84 #else /* CONFOPT_GETTIMEOFDAY */
85 
86     r = (double) time(NULL);
87 
88 #endif /* CONFOPT_GETTIMEOFDAY */
89 
90     return r;
91 }
92 
93 
mpdm_random(mpdm_t v)94 mpdm_t mpdm_random(mpdm_t v)
95 {
96     static unsigned int seed = 0;
97     mpdm_t r = NULL;
98 
99     if (mpdm_type(v) == MPDM_TYPE_ARRAY)
100         r = mpdm_get(v, mpdm_random(MPDM_I(mpdm_size(v))));
101     else {
102         int range = mpdm_ival(v);
103         int v = 0;
104 
105         /* crappy random seed */
106         if (seed == 0) {
107             time_t t = time(NULL);
108             seed = t ^ (t << 16);
109         }
110 
111         seed = (seed * 58321) + 11113;
112         v = (seed >> 16);
113 
114         if (range)
115             r = MPDM_I(v % range);
116         else
117             r = MPDM_R((v & 0xffff) / 65536.0);
118     }
119 
120     return r;
121 }
122 
123 
124 /** mutexes **/
125 
vc_mutex_destroy(mpdm_t v)126 static mpdm_t vc_mutex_destroy(mpdm_t v)
127 {
128 #ifdef CONFOPT_WIN32
129     HANDLE *h = (HANDLE *) v->data;
130 
131     CloseHandle(*h);
132 #endif
133 
134 #ifdef CONFOPT_PTHREADS
135     pthread_mutex_t *m = (pthread_mutex_t *) v->data;
136 
137     pthread_mutex_destroy(m);
138 #endif
139 
140     v->data = NULL;
141 
142     return v;
143 }
144 
145 
146 /**
147  * mpdm_new_mutex - Creates a new mutex.
148  *
149  * Creates a new mutex.
150  * [Threading]
151  */
mpdm_new_mutex(void)152 mpdm_t mpdm_new_mutex(void)
153 {
154     char *ptr = NULL;
155     int size = 0;
156 
157 #ifdef CONFOPT_WIN32
158     HANDLE h;
159 
160     h = CreateMutex(NULL, FALSE, NULL);
161 
162     if (h != NULL) {
163         size = sizeof(h);
164         ptr = (char *) &h;
165     }
166 #endif
167 
168 #ifdef CONFOPT_PTHREADS
169     pthread_mutex_t m;
170 
171     if (pthread_mutex_init(&m, NULL) == 0) {
172         size = sizeof(m);
173         ptr = (char *) &m;
174     }
175 
176 #endif
177 
178     return MPDM_C(MPDM_TYPE_MUTEX, ptr, size);
179 }
180 
181 
182 /**
183  * mpdm_mutex_lock - Locks a mutex.
184  * @mutex: the mutex to be locked
185  *
186  * Locks a mutex. If the mutex is not already locked,
187  * it waits until it is.
188  * [Threading]
189  */
mpdm_mutex_lock(mpdm_t mutex)190 void mpdm_mutex_lock(mpdm_t mutex)
191 {
192 #ifdef CONFOPT_WIN32
193     HANDLE *h = (HANDLE *) mutex->data;
194 
195     WaitForSingleObject(*h, INFINITE);
196 #endif
197 
198 #ifdef CONFOPT_PTHREADS
199     pthread_mutex_t *m = (pthread_mutex_t *) mutex->data;
200 
201     pthread_mutex_lock(m);
202 #endif
203 }
204 
205 
206 /**
207  * mpdm_mutex_unlock - Unlocks a mutex.
208  * @mutex: the mutex to be unlocked
209  *
210  * Unlocks a previously locked mutex. The thread
211  * unlocking the mutex must be the one who locked it.
212  * [Threading]
213  */
mpdm_mutex_unlock(mpdm_t mutex)214 void mpdm_mutex_unlock(mpdm_t mutex)
215 {
216 #ifdef CONFOPT_WIN32
217     HANDLE *h = (HANDLE *) mutex->data;
218 
219     ReleaseMutex(*h);
220 #endif
221 
222 #ifdef CONFOPT_PTHREADS
223     pthread_mutex_t *m = (pthread_mutex_t *) mutex->data;
224 
225     pthread_mutex_unlock(m);
226 #endif
227 }
228 
229 
230 /** semaphores **/
231 
vc_semaphore_destroy(mpdm_t v)232 static mpdm_t vc_semaphore_destroy(mpdm_t v)
233 {
234 #ifdef CONFOPT_WIN32
235     HANDLE *h = (HANDLE *) v->data;
236 
237     CloseHandle(*h);
238 #endif
239 
240 #ifdef CONFOPT_POSIXSEMS
241     sem_t *s = (sem_t *) v->data;
242 
243     sem_destroy(s);
244 #endif
245 
246     v->data = NULL;
247 
248     return v;
249 }
250 
251 
252 /**
253  * mpdm_new_semaphore - Creates a new semaphore.
254  * @init_value: the initial value of the semaphore.
255  *
256  * Creates a new semaphore with an @init_value.
257  * [Threading]
258  */
mpdm_new_semaphore(int init_value)259 mpdm_t mpdm_new_semaphore(int init_value)
260 {
261     char *ptr = NULL;
262     int size = 0;
263 
264 #ifdef CONFOPT_WIN32
265     HANDLE h;
266 
267     if ((h = CreateSemaphore(NULL, init_value, 0x7fffffff, NULL)) != NULL) {
268         size = sizeof(h);
269         ptr = (char *) &h;
270     }
271 
272 #endif
273 
274 #ifdef CONFOPT_POSIXSEMS
275     sem_t s;
276 
277     if (sem_init(&s, 0, init_value) == 0) {
278         size = sizeof(s);
279         ptr = (char *) &s;
280     }
281 
282 #endif
283 
284     return MPDM_C(MPDM_TYPE_SEMAPHORE, ptr, size);
285 }
286 
287 
288 /**
289  * mpdm_semaphore_wait - Waits for a semaphore to be ready.
290  * @sem: the semaphore to wait onto
291  *
292  * Waits for the value of a semaphore to be > 0. If it's
293  * not, the thread waits until it is.
294  * [Threading]
295  */
mpdm_semaphore_wait(mpdm_t sem)296 void mpdm_semaphore_wait(mpdm_t sem)
297 {
298 #ifdef CONFOPT_WIN32
299     HANDLE *h = (HANDLE *) sem->data;
300 
301     WaitForSingleObject(*h, INFINITE);
302 #endif
303 
304 #ifdef CONFOPT_POSIXSEMS
305     sem_t *s = (sem_t *) sem->data;
306 
307     sem_wait(s);
308 #endif
309 }
310 
311 
312 /**
313  * mpdm_semaphore_post - Increments the value of a semaphore.
314  * @sem: the semaphore to increment
315  *
316  * Increments by 1 the value of a semaphore.
317  * [Threading]
318  */
mpdm_semaphore_post(mpdm_t sem)319 void mpdm_semaphore_post(mpdm_t sem)
320 {
321 #ifdef CONFOPT_WIN32
322     HANDLE *h = (HANDLE *) sem->data;
323 
324     ReleaseSemaphore(*h, 1, NULL);
325 #endif
326 
327 #ifdef CONFOPT_POSIXSEMS
328     sem_t *s = (sem_t *) sem->data;
329 
330     sem_post(s);
331 #endif
332 }
333 
334 
335 /** threads **/
336 
vc_thread_destroy(mpdm_t v)337 static mpdm_t vc_thread_destroy(mpdm_t v)
338 {
339     v->data = NULL;
340 
341     return v;
342 }
343 
344 
thread_caller(mpdm_t a)345 static void thread_caller(mpdm_t a)
346 {
347     /* ignore return value */
348     mpdm_void(mpdm_exec(mpdm_get_i(a, 0), mpdm_get_i(a, 1), mpdm_get_i(a, 2)));
349 
350     /* was referenced in mpdm_exec_thread() */
351     mpdm_unref(a);
352 }
353 
354 
355 #ifdef CONFOPT_WIN32
win32_thread(LPVOID param)356 DWORD WINAPI win32_thread(LPVOID param)
357 {
358     thread_caller((mpdm_t) param);
359 
360     return 0;
361 }
362 #endif
363 
364 #ifdef CONFOPT_PTHREADS
pthreads_thread(void * args)365 void *pthreads_thread(void *args)
366 {
367     thread_caller((mpdm_t) args);
368 
369     return NULL;
370 }
371 #endif
372 
373 /**
374  * mpdm_exec_thread - Runs an executable value in a new thread.
375  * @c: the executable value
376  * @args: executable arguments
377  * @ctxt: the context
378  *
379  * Runs the @c executable value in a new thread. The code
380  * starts executing immediately. The @args and @ctxt arguments
381  * are sent to the executable value as arguments.
382  *
383  * Returns a handle for the thread.
384  * [Threading]
385  */
mpdm_exec_thread(mpdm_t c,mpdm_t args,mpdm_t ctxt)386 mpdm_t mpdm_exec_thread(mpdm_t c, mpdm_t args, mpdm_t ctxt)
387 {
388     mpdm_t a;
389     char *ptr = NULL;
390     int size = 0;
391 
392     if (ctxt == NULL)
393         ctxt = MPDM_A(0);
394 
395     /* to be unreferenced at thread stop */
396     a = mpdm_ref(MPDM_A(3));
397 
398     mpdm_set_i(a, c, 0);
399     mpdm_set_i(a, args, 1);
400     mpdm_set_i(a, ctxt, 2);
401 
402 #ifdef CONFOPT_WIN32
403     HANDLE t;
404 
405     t = CreateThread(NULL, 0, win32_thread, a, 0, NULL);
406 
407     if (t != NULL) {
408         size = sizeof(t);
409         ptr = (char *) &t;
410     }
411 
412 #endif
413 
414 #ifdef CONFOPT_PTHREADS
415     pthread_t pt;
416 
417     if (pthread_create(&pt, NULL, pthreads_thread, a) == 0) {
418         size = sizeof(pthread_t);
419         ptr = (char *) &pt;
420     }
421 
422 #endif
423 
424     return MPDM_C(MPDM_TYPE_THREAD, ptr, size);
425 }
426 
427 
428 /** zlib functions **/
429 
mpdm_gzip_inflate(unsigned char * cbuf,size_t cz,size_t * dz)430 unsigned char *mpdm_gzip_inflate(unsigned char *cbuf, size_t cz, size_t *dz)
431 {
432     unsigned char *dbuf = NULL;
433 
434 #ifdef CONFOPT_ZLIB
435 
436     if (cbuf[0] == 0x1f && cbuf[1] == 0x8b) {
437         z_stream d_stream;
438         int err;
439 
440         /* size % 2^32 is at the end */
441         *dz = cbuf[cz - 1];
442         *dz = (*dz * 256) + cbuf[cz - 2];
443         *dz = (*dz * 256) + cbuf[cz - 3];
444         *dz = (*dz * 256) + cbuf[cz - 4];
445 
446         dbuf = calloc(*dz + 1, 1);
447 
448         memset(&d_stream, '\0', sizeof(d_stream));
449 
450         d_stream.next_in   = cbuf;
451         d_stream.avail_in  = cz;
452         d_stream.next_out  = dbuf;
453         d_stream.avail_out = *dz;
454 
455         if ((err = inflateInit2(&d_stream, 16 + MAX_WBITS)) == Z_OK) {
456             while (err != Z_STREAM_END && err >= 0)
457                 err = inflate(&d_stream, Z_FINISH);
458 
459             err = inflateEnd(&d_stream);
460         }
461 
462         if (err != Z_OK || *dz != d_stream.total_out)
463             dbuf = realloc(dbuf, 0);
464     }
465 
466 #endif /* CONFOPT_ZLIB */
467 
468     return dbuf;
469 }
470 
471 
472 /** tar archives **/
473 
mpdm_read_tar_mem(const char * fn,const char * tar,const char * tar_e,size_t * z)474 unsigned char *mpdm_read_tar_mem(const char *fn, const char *tar,
475                                  const char *tar_e, size_t *z)
476 {
477     unsigned char *data = NULL;
478 
479     while (!data && tar < tar_e && *tar) {
480         sscanf(&tar[124], "%lo", (long *)z);
481 
482         if (strcmp(fn, tar) == 0)
483             data = memcpy(calloc(*z + 1, 1), tar + 512, *z);
484         else
485             tar += (1 + ((*z + 511) / 512)) * 512;
486     }
487 
488     return data;
489 }
490 
491 
mpdm_read_tar_file(const char * fn,FILE * f,size_t * z)492 unsigned char *mpdm_read_tar_file(const char *fn, FILE *f, size_t *z)
493 {
494     unsigned char *data = NULL;
495     char tar[512];
496 
497     while (!data && fread(tar, sizeof(tar), 1, f) && *tar) {
498         sscanf(&tar[124], "%lo", (long *)z);
499 
500         if (strcmp(fn, tar) == 0) {
501             data = calloc(*z + 1, 1);
502             if (!fread(data, *z, 1, f))
503                 data = realloc(data, 0);
504         }
505         else
506             fseek(f, ((*z + 511) / 512) * 512, 1);
507     }
508 
509     return data;
510 }
511 
512 
513 /** zip archives **/
514 
515 struct zip_hdr {
516     unsigned int sig;
517     unsigned short min_ver;
518     unsigned short gp_flag;
519     unsigned short comp_meth;
520     unsigned short mtime;
521     unsigned short mdate;
522     unsigned int crc32;
523     unsigned char csz[4];
524     unsigned char usz[4];
525     unsigned char fnsz[2];
526     unsigned char xfsz[2];
527 } __attribute__ ((__packed__));
528 
529 
530 int puff(unsigned char *dest,           /* pointer to destination pointer */
531          unsigned long *destlen,        /* amount of output space */
532          const unsigned char *source,   /* pointer to source data pointer */
533          unsigned long *sourcelen);     /* amount of input available */
534 
mpdm_read_zip_mem(const char * fn,const char * zip,const char * zip_e,size_t * z)535 unsigned char *mpdm_read_zip_mem(const char *fn, const char *zip,
536                                  const char *zip_e, size_t *z)
537 {
538     unsigned char *data = NULL;
539     int sz = strlen(fn);
540 
541     while (!data && zip < zip_e) {
542         struct zip_hdr *hdr = (struct zip_hdr *)zip;
543         unsigned char *cdata;
544         unsigned long int usz;
545         unsigned long int csz;
546         unsigned short fnsz;
547         unsigned short xfsz;
548 
549         usz  = hdr->usz[3] << 24 | hdr->usz[2] << 16 | hdr->usz[1] << 8 | hdr->usz[0];
550         csz  = hdr->csz[3] << 24 | hdr->csz[2] << 16 | hdr->csz[1] << 8 | hdr->csz[0];
551         fnsz = hdr->fnsz[1] << 8 | hdr->fnsz[0];
552         xfsz = hdr->xfsz[1] << 8 | hdr->xfsz[0];
553 
554         if (zip[0] != 'P' || zip[1] != 'K' || fnsz == 0)
555             break;
556 
557         cdata = (unsigned char *)zip + sizeof(struct zip_hdr) + fnsz + xfsz;
558 
559         if (sz == fnsz && memcmp(&zip[30], fn, fnsz) == 0) {
560             data = calloc(usz + 1, 1);
561 
562             if (hdr->comp_meth == 0)
563                 memcpy(cdata, data, usz);
564             else
565             if (hdr->comp_meth == 8)
566                 puff(data, &usz, cdata, &csz);
567             else {
568                 data = realloc(data, 0);
569                 break;
570             }
571 
572             *z = usz;
573         }
574         else
575             zip = (char *)cdata + csz;
576     }
577 
578     return data;
579 }
580 
581 
mpdm_read_zip_file(const char * fn,FILE * f,size_t * z)582 unsigned char *mpdm_read_zip_file(const char *fn, FILE *f, size_t *z)
583 {
584     unsigned char *data = NULL;
585 
586     return data;
587 }
588 
589 
590 /** 'generic' archives **/
591 
mpdm_read_arch_mem(const char * fn,const char * arch,const char * arch_e,size_t * z)592 unsigned char *mpdm_read_arch_mem(const char *fn, const char *arch,
593                                   const char *arch_e, size_t *z)
594 {
595     unsigned char *data = NULL;
596 
597     if (arch && arch[0] == 'P' && arch[1] == 'K')
598         data = mpdm_read_zip_mem(fn, arch, arch_e, z);
599     else
600         data = mpdm_read_tar_mem(fn, arch, arch_e, z);
601 
602     return data;
603 }
604 
605 
mpdm_read_arch_mem_s(mpdm_t fn,const char * arch,const char * arch_e)606 mpdm_t mpdm_read_arch_mem_s(mpdm_t fn, const char *arch, const char *arch_e)
607 {
608     mpdm_t f, r = NULL;
609     unsigned char *data;
610     size_t z;
611 
612     mpdm_ref(fn);
613 
614     /* convert to mbs */
615     f = mpdm_ref(MPDM_2MBS(mpdm_string(fn)));
616 
617     if ((data = mpdm_read_arch_mem((const char *)f->data, arch, arch_e, &z)) != NULL) {
618         r = MPDM_MBS((char *)data);
619         free(data);
620     }
621 
622     mpdm_unref(f);
623     mpdm_unref(fn);
624 
625     return r;
626 }
627 
628 
mpdm_read_arch_file(const char * fn,FILE * f,size_t * z)629 unsigned char *mpdm_read_arch_file(const char *fn, FILE *f, size_t *z)
630 {
631     unsigned char *data = NULL;
632     char sig[2];
633 
634     /* read signature */
635     fread(sig, sizeof(sig), 1, f);
636     rewind(f);
637 
638     if (sig[0] == 'P' && sig[1] == 'K')
639         data = mpdm_read_zip_file(fn, f, z);
640     else
641         data = mpdm_read_tar_file(fn, f, z);
642 
643     return data;
644 }
645 
646 
mpdm_read_arch_file_s(mpdm_t fn,mpdm_t fd)647 mpdm_t mpdm_read_arch_file_s(mpdm_t fn, mpdm_t fd)
648 {
649     mpdm_t fs, r = NULL;
650     FILE *f = mpdm_get_filehandle(fd);
651 
652     mpdm_ref(fn);
653 
654     /* convert to mbs */
655     fs = mpdm_ref(MPDM_2MBS(mpdm_string(fn)));
656 
657     if (f) {
658         unsigned char *data = NULL;
659         size_t z;
660 
661         if ((data = mpdm_read_arch_file((const char *)fs->data, f, &z)) != NULL) {
662             r = MPDM_MBS((char *)data);
663             free(data);
664         }
665     }
666 
667     mpdm_unref(fs);
668     mpdm_unref(fn);
669 
670     return r;
671 }
672 
673 
674 /** other utilities **/
675 
676 void md5_simple(void *output, const void *input, unsigned long size);
677 
mpdm_md5(mpdm_t v)678 mpdm_t mpdm_md5(mpdm_t v)
679 {
680     mpdm_t r = NULL;
681 
682     mpdm_ref(v);
683 
684     if (mpdm_type(v) == MPDM_TYPE_STRING) {
685         char *ptr;
686         unsigned char md5[16];
687         char md5_s[33];
688         int n;
689 
690         ptr = mpdm_wcstombs(mpdm_string(v), &n);
691 
692         md5_simple(md5, ptr, n);
693 
694         for (n = 0; n < sizeof(md5); n++)
695             sprintf(&md5_s[n * 2], "%02x", md5[n]);
696         md5_s[32] = '\0';
697 
698         free(ptr);
699 
700         r = MPDM_MBS(md5_s);
701     }
702 
703     mpdm_unref(v);
704 
705     return r;
706 }
707 
708 
709 /** data vc **/
710 
711 struct mpdm_type_vc mpdm_vc_mutex = { /* VC */
712     L"mutex",               /* name */
713     vc_mutex_destroy,       /* destroy */
714     vc_default_is_true,     /* is_true */
715     vc_default_count,       /* count */
716     vc_default_get_i,       /* get_i */
717     vc_default_get,         /* get */
718     vc_default_string,      /* string */
719     vc_default_del_i,       /* del_i */
720     vc_default_del,         /* del */
721     vc_default_set_i,       /* set_i */
722     vc_default_set,         /* set */
723     vc_default_exec,        /* exec */
724     vc_default_iterator,    /* iterator */
725     vc_default_map          /* map */
726 };
727 
728 struct mpdm_type_vc mpdm_vc_semaphore = { /* VC */
729     L"semaphore",           /* name */
730     vc_semaphore_destroy,   /* destroy */
731     vc_default_is_true,     /* is_true */
732     vc_default_count,       /* count */
733     vc_default_get_i,       /* get_i */
734     vc_default_get,         /* get */
735     vc_default_string,      /* string */
736     vc_default_del_i,       /* del_i */
737     vc_default_del,         /* del */
738     vc_default_set_i,       /* set_i */
739     vc_default_set,         /* set */
740     vc_default_exec,        /* exec */
741     vc_default_iterator,    /* iterator */
742     vc_default_map          /* map */
743 };
744 
745 struct mpdm_type_vc mpdm_vc_thread = { /* VC */
746     L"thread",              /* name */
747     vc_thread_destroy,      /* destroy */
748     vc_default_is_true,     /* is_true */
749     vc_default_count,       /* count */
750     vc_default_get_i,       /* get_i */
751     vc_default_get,         /* get */
752     vc_default_string,      /* string */
753     vc_default_del_i,       /* del_i */
754     vc_default_del,         /* del */
755     vc_default_set_i,       /* set_i */
756     vc_default_set,         /* set */
757     vc_default_exec,        /* exec */
758     vc_default_iterator,    /* iterator */
759     vc_default_map          /* map */
760 };
761