Statistics
| Branch: | Revision:

janus-gateway / utils.c @ 1f44763c

History | View | Annotate | Download (13 KB)

1 5e9e29e0 meetecho
/*! \file    utils.c
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3 9d11ac50 meetecho
 * \copyright GNU General Public License v3
4 5e9e29e0 meetecho
 * \brief    Utilities and helpers
5
 * \details  Implementations of a few methods that may be of use here
6
 * and there in the code.
7 927d88c5 pallab-gain
 * 
8 5e9e29e0 meetecho
 * \ingroup core
9
 * \ref core
10
 */
11
12 3f0b4067 meetecho
#include <stdlib.h>
13
#include <string.h>
14 5fa9a305 meetecho
#include <sys/stat.h>
15
#include <errno.h>
16 ea6da8ee jswirl
#include <fcntl.h>
17 4ea57952 Lorenzo Miniero
#include <sys/file.h>
18 f9a38d16 Saúl Ibarra Corretgé
#include <sys/types.h>
19 4ea57952 Lorenzo Miniero
#include <unistd.h>
20 5fa9a305 meetecho
21 5e9e29e0 meetecho
#include "utils.h"
22 5fa9a305 meetecho
#include "debug.h"
23 5e9e29e0 meetecho
24 b1c60d34 Yulius Tjahjadi
#if __MACH__
25
#include "mach_gettime.h"
26
#endif
27
28 77992057 Philip Withnall
gint64 janus_get_monotonic_time(void) {
29 5e9e29e0 meetecho
        struct timespec ts;
30
        clock_gettime (CLOCK_MONOTONIC, &ts);
31
        return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000));
32
}
33 b4b5a20a meetecho
34 8caad21f Lorenzo Miniero
gint64 janus_get_real_time(void) {
35
        struct timespec ts;
36
        clock_gettime (CLOCK_REALTIME, &ts);
37
        return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000));
38
}
39
40 6dd1c12c meetecho
gboolean janus_is_true(const char *value) {
41
        return value && (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"));
42
}
43 b4b5a20a meetecho
44 a60399ff meetecho
gboolean janus_strcmp_const_time(const void *str1, const void *str2) {
45
        if(str1 == NULL || str2 == NULL)
46 84c0ec3d meetecho
                return FALSE;
47
        const unsigned char *string1 = (const unsigned char *)str1;
48
        const unsigned char *string2 = (const unsigned char *)str2;
49 a60399ff meetecho
        size_t maxlen = strlen((char *)string1);
50
        if(strlen((char *)string2) > maxlen)
51
                maxlen = strlen((char *)string2);
52 1f067658 Lorenzo Miniero
        unsigned char *buf1 = g_malloc0(maxlen+1);
53 a60399ff meetecho
        memset(buf1, 0, maxlen);
54
        memcpy(buf1, string1, strlen(str1));
55 1f067658 Lorenzo Miniero
        unsigned char *buf2 = g_malloc0(maxlen+1);
56 a60399ff meetecho
        memset(buf2, 0, maxlen);
57
        memcpy(buf2, string2, strlen(str2));
58 84c0ec3d meetecho
        unsigned char result = 0;
59
        size_t i = 0;
60 a60399ff meetecho
        for (i = 0; i < maxlen; i++) {
61
                result |= buf1[i] ^ buf2[i];
62 84c0ec3d meetecho
        }
63 a60399ff meetecho
        g_free(buf1);
64
        buf1 = NULL;
65
        g_free(buf2);
66
        buf2 = NULL;
67 84c0ec3d meetecho
        return result == 0;
68
}
69
70 d236f0e9 Lorenzo Miniero
guint32 janus_random_uint32(void) {
71
        return g_random_int();
72
}
73
74
guint64 janus_random_uint64(void) {
75
        /*
76
         * FIXME This needs to be improved, and use something that generates
77
         * more strongly random stuff... using /dev/urandom is probably not
78
         * a good idea, as we don't want to make it harder to cross compile Janus
79
         *
80
         * TODO Look into what libssl and/or libcrypto provide in that respect
81
         *
82
         * PS: JavaScript only supports integer up to 2^53, so we need to
83
         * make sure the number is below 9007199254740992 for safety
84
         */
85
        guint64 num = g_random_int() & 0x1FFFFF;
86
        num = (num << 32) | g_random_int();
87
        return num;
88
}
89
90
guint64 *janus_uint64_dup(guint64 num) {
91 bc05610a Lorenzo Miniero
        guint64 *numdup = g_malloc0(sizeof(guint64));
92
        memcpy(numdup, &num, sizeof(num));
93
        return numdup;
94 d236f0e9 Lorenzo Miniero
}
95
96 b4b5a20a meetecho
void janus_flags_reset(janus_flags *flags) {
97
        if(flags != NULL)
98
                *flags = 0;
99
}
100
101
void janus_flags_set(janus_flags *flags, uint32_t flag) {
102
        if(flags != NULL) {
103
                *flags |= flag;
104
        }
105
}
106
107
void janus_flags_clear(janus_flags *flags, uint32_t flag) {
108
        if(flags != NULL) {
109
                *flags &= ~(flag);
110
        }
111
}
112
113
gboolean janus_flags_is_set(janus_flags *flags, uint32_t flag) {
114
        if(flags != NULL) {
115
                uint32_t bit = *flags & flag;
116
                return (bit != 0);
117
        }
118
        return FALSE;
119
}
120 3f0b4067 meetecho
121 638695a1 Lorenzo Miniero
/* Easy way to replace multiple occurrences of a string with another */
122 46feb4e8 Philip Withnall
char *janus_string_replace(char *message, const char *old_string, const char *new_string)
123 3f0b4067 meetecho
{
124 46feb4e8 Philip Withnall
        if(!message || !old_string || !new_string)
125 3f0b4067 meetecho
                return NULL;
126 46feb4e8 Philip Withnall
127 e12d163e meetecho
        if(!strstr(message, old_string)) {        /* Nothing to be done (old_string is not there) */
128 3f0b4067 meetecho
                return message;
129
        }
130 e12d163e meetecho
        if(!strcmp(old_string, new_string)) {        /* Nothing to be done (old_string=new_string) */
131 3f0b4067 meetecho
                return message;
132
        }
133 e12d163e meetecho
        if(strlen(old_string) == strlen(new_string)) {        /* Just overwrite */
134 3f0b4067 meetecho
                char *outgoing = message;
135 e12d163e meetecho
                char *pos = strstr(outgoing, old_string), *tmp = NULL;
136 3f0b4067 meetecho
                int i = 0;
137
                while(pos) {
138
                        i++;
139 e12d163e meetecho
                        memcpy(pos, new_string, strlen(new_string));
140
                        pos += strlen(old_string);
141
                        tmp = strstr(pos, old_string);
142 3f0b4067 meetecho
                        pos = tmp;
143
                }
144
                return outgoing;
145
        } else {        /* We need to resize */
146 1f067658 Lorenzo Miniero
                char *outgoing = g_strdup(message);
147
                g_free(message);
148 3f0b4067 meetecho
                if(outgoing == NULL) {
149
                        return NULL;
150
                }
151 e12d163e meetecho
                int diff = strlen(new_string) - strlen(old_string);
152 3f0b4067 meetecho
                /* Count occurrences */
153
                int counter = 0;
154 e12d163e meetecho
                char *pos = strstr(outgoing, old_string), *tmp = NULL;
155 3f0b4067 meetecho
                while(pos) {
156
                        counter++;
157 e12d163e meetecho
                        pos += strlen(old_string);
158
                        tmp = strstr(pos, old_string);
159 3f0b4067 meetecho
                        pos = tmp;
160
                }
161 e12d163e meetecho
                uint16_t old_stringlen = strlen(outgoing)+1, new_stringlen = old_stringlen + diff*counter;
162 3f0b4067 meetecho
                if(diff > 0) {        /* Resize now */
163 1f067658 Lorenzo Miniero
                        tmp = g_realloc(outgoing, new_stringlen);
164 46feb4e8 Philip Withnall
                        if(!tmp) {
165 1f067658 Lorenzo Miniero
                                g_free(outgoing);
166 3f0b4067 meetecho
                                return NULL;
167 46feb4e8 Philip Withnall
                        }
168 3f0b4067 meetecho
                        outgoing = tmp;
169
                }
170
                /* Replace string */
171 e12d163e meetecho
                pos = strstr(outgoing, old_string);
172 3f0b4067 meetecho
                while(pos) {
173 e12d163e meetecho
                        if(diff > 0) {        /* Move to the right (new_string is larger than old_string) */
174 3f0b4067 meetecho
                                uint16_t len = strlen(pos)+1;
175
                                memmove(pos + diff, pos, len);
176 e12d163e meetecho
                                memcpy(pos, new_string, strlen(new_string));
177
                                pos += strlen(new_string);
178
                                tmp = strstr(pos, old_string);
179
                        } else {        /* Move to the left (new_string is smaller than old_string) */
180 3f0b4067 meetecho
                                uint16_t len = strlen(pos - diff)+1;
181
                                memmove(pos, pos - diff, len);
182 e12d163e meetecho
                                memcpy(pos, new_string, strlen(new_string));
183
                                pos += strlen(old_string);
184
                                tmp = strstr(pos, old_string);
185 3f0b4067 meetecho
                        }
186
                        pos = tmp;
187
                }
188
                if(diff < 0) {        /* We skipped the resize previously (shrinking memory) */
189 1f067658 Lorenzo Miniero
                        tmp = g_realloc(outgoing, new_stringlen);
190 46feb4e8 Philip Withnall
                        if(!tmp) {
191 1f067658 Lorenzo Miniero
                                g_free(outgoing);
192 3f0b4067 meetecho
                                return NULL;
193 46feb4e8 Philip Withnall
                        }
194 3f0b4067 meetecho
                        outgoing = tmp;
195
                }
196
                outgoing[strlen(outgoing)] = '\0';
197
                return outgoing;
198
        }
199
}
200 5fa9a305 meetecho
201
int janus_mkdir(const char *dir, mode_t mode) {
202
        char tmp[256];
203
        char *p = NULL;
204
        size_t len;
205
206
        int res = 0;
207
        g_snprintf(tmp, sizeof(tmp), "%s", dir);
208
        len = strlen(tmp);
209
        if(tmp[len - 1] == '/')
210
                tmp[len - 1] = 0;
211
        for(p = tmp + 1; *p; p++) {
212
                if(*p == '/') {
213
                        *p = 0;
214
                        res = mkdir(tmp, mode);
215
                        if(res != 0 && errno != EEXIST) {
216
                                JANUS_LOG(LOG_ERR, "Error creating folder %s\n", tmp);
217
                                return res;
218
                        }
219
                        *p = '/';
220
                }
221
        }
222
        res = mkdir(tmp, mode);
223
        if(res != 0 && errno != EEXIST)
224
                return res;
225
        return 0;
226
}
227
228 b29447b1 Lorenzo Miniero
int janus_get_codec_pt(const char *sdp, const char *codec) {
229
        if(!sdp || !codec)
230 5fa9a305 meetecho
                return -1;
231 b29447b1 Lorenzo Miniero
        int video = 0;
232
        const char *format = NULL, *format2 = NULL;
233
        if(!strcasecmp(codec, "opus")) {
234
                video = 0;
235
                format = "opus/48000/2";
236
                format2 = "OPUS/48000/2";
237
        } else if(!strcasecmp(codec, "pcmu")) {
238
                /* We know the payload type is 0: we just need to make sure it's there */
239
                video = 0;
240
                format = "pcmu/8000";
241
                format2 = "PCMU/8000";
242
        } else if(!strcasecmp(codec, "pcma")) {
243
                /* We know the payload type is 8: we just need to make sure it's there */
244
                video = 0;
245
                format = "pcma/8000";
246
                format2 = "PCMA/8000";
247 183d715f Lorenzo Miniero
        } else if(!strcasecmp(codec, "g722")) {
248
                /* We know the payload type is 9: we just need to make sure it's there */
249
                video = 0;
250
                format = "g722/8000";
251
                format2 = "G722/8000";
252 b29447b1 Lorenzo Miniero
        } else if(!strcasecmp(codec, "isac16")) {
253
                video = 0;
254
                format = "isac/16000";
255
                format2 = "ISAC/16000";
256
        } else if(!strcasecmp(codec, "isac32")) {
257
                video = 0;
258
                format = "isac/32000";
259
                format2 = "ISAC/32000";
260
        } else if(!strcasecmp(codec, "vp8")) {
261
                video = 1;
262
                format = "vp8/90000";
263
                format2 = "VP8/90000";
264
        } else if(!strcasecmp(codec, "vp9")) {
265
                video = 1;
266
                format = "vp9/90000";
267
                format2 = "VP9/90000";
268
        } else if(!strcasecmp(codec, "h264")) {
269
                video = 1;
270
                format = "h264/90000";
271
                format2 = "H264/90000";
272
        } else {
273
                JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
274 fd8cb2d5 pallab-gain
                return -1;
275
        }
276 b29447b1 Lorenzo Miniero
        /* First of all, let's check if the codec is there */
277
        if(!video) {
278
                if(!strstr(sdp, "m=audio") || (!strstr(sdp, format) && !strstr(sdp, format2)))
279
                        return -2;
280
        } else {
281
                if(!strstr(sdp, "m=video") || (!strstr(sdp, format) && !strstr(sdp, format2)))
282
                        return -2;
283 5fa9a305 meetecho
        }
284 b29447b1 Lorenzo Miniero
        char rtpmap[50], rtpmap2[50];
285
        g_snprintf(rtpmap, 50, "a=rtpmap:%%d %s", format);
286
        g_snprintf(rtpmap2, 50, "a=rtpmap:%%d %s", format2);
287
        /* Look for the mapping */
288
        const char *line = strstr(sdp, video ? "m=video" : "m=audio");
289 fc23d811 Lorenzo Miniero
        while(line) {
290
                char *next = strchr(line, '\n');
291
                if(next) {
292
                        *next = '\0';
293 b29447b1 Lorenzo Miniero
                        if(strstr(line, "a=rtpmap") && strstr(line, format)) {
294 fc23d811 Lorenzo Miniero
                                /* Gotcha! */
295
                                int pt = 0;
296 d7086be9 Lorenzo Miniero
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
297 b29447b1 Lorenzo Miniero
                                if(sscanf(line, rtpmap, &pt) == 1) {
298 fc23d811 Lorenzo Miniero
                                        *next = '\n';
299
                                        return pt;
300
                                }
301 b29447b1 Lorenzo Miniero
                        } else if(strstr(line, "a=rtpmap") && strstr(line, format2)) {
302 fc23d811 Lorenzo Miniero
                                /* Gotcha! */
303
                                int pt = 0;
304 b29447b1 Lorenzo Miniero
                                if(sscanf(line, rtpmap2, &pt) == 1) {
305 d7086be9 Lorenzo Miniero
#pragma GCC diagnostic warning "-Wformat-nonliteral"
306 fc23d811 Lorenzo Miniero
                                        *next = '\n';
307
                                        return pt;
308
                                }
309
                        }
310
                        *next = '\n';
311
                }
312
                line = next ? (next+1) : NULL;
313
        }
314
        return -3;
315
}
316
317 b29447b1 Lorenzo Miniero
const char *janus_get_codec_from_pt(const char *sdp, int pt) {
318
        if(!sdp || pt < 0)
319
                return NULL;
320
        if(pt == 0)
321
                return "pcmu";
322
        if(pt == 8)
323
                return "pcma";
324 183d715f Lorenzo Miniero
        if(pt == 9)
325
                return "g722";
326 b29447b1 Lorenzo Miniero
        /* Look for the mapping */
327
        char rtpmap[50];
328
        g_snprintf(rtpmap, 50, "a=rtpmap:%d ", pt);
329
        const char *line = strstr(sdp, "m=");
330 fc23d811 Lorenzo Miniero
        while(line) {
331
                char *next = strchr(line, '\n');
332
                if(next) {
333
                        *next = '\0';
334 b29447b1 Lorenzo Miniero
                        if(strstr(line, rtpmap)) {
335 fc23d811 Lorenzo Miniero
                                /* Gotcha! */
336 b29447b1 Lorenzo Miniero
                                char name[100];
337
                                if(sscanf(line, "a=rtpmap:%d %s", &pt, name) == 2) {
338 fc23d811 Lorenzo Miniero
                                        *next = '\n';
339 b29447b1 Lorenzo Miniero
                                        if(strstr(name, "vp8") || strstr(name, "VP8"))
340 1c2ef2e1 Lorenzo Miniero
                                                return "vp8";
341 b29447b1 Lorenzo Miniero
                                        if(strstr(name, "vp9") || strstr(name, "VP9"))
342
                                                return "vp9";
343
                                        if(strstr(name, "h264") || strstr(name, "H264"))
344
                                                return "h264";
345
                                        if(strstr(name, "opus") || strstr(name, "OPUS"))
346
                                                return "opus";
347
                                        if(strstr(name, "pcmu") || strstr(name, "PMCU"))
348
                                                return "pcmu";
349
                                        if(strstr(name, "pcma") || strstr(name, "PMCA"))
350
                                                return "pcma";
351 183d715f Lorenzo Miniero
                                        if(strstr(name, "g722") || strstr(name, "G722"))
352
                                                return "g722";
353 b29447b1 Lorenzo Miniero
                                        if(strstr(name, "isac/16") || strstr(name, "ISAC/16"))
354
                                                return "isac16";
355
                                        if(strstr(name, "isac/32") || strstr(name, "ISAC/32"))
356
                                                return "isac32";
357
                                        JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", name);
358
                                        return NULL;
359 fc23d811 Lorenzo Miniero
                                }
360
                        }
361
                        *next = '\n';
362
                }
363
                line = next ? (next+1) : NULL;
364
        }
365 b29447b1 Lorenzo Miniero
        return NULL;
366 fc23d811 Lorenzo Miniero
}
367
368 4ea57952 Lorenzo Miniero
/* PID file management */
369
static char *pidfile = NULL;
370
static int pidfd = -1;
371
static FILE *pidf = NULL;
372
int janus_pidfile_create(const char *file) {
373
        if(file == NULL)
374
                return 0;
375
        pidfile = g_strdup(file);
376
        /* Try creating a PID file (or opening an existing one) */
377
        pidfd = open(pidfile, O_RDWR|O_CREAT, 0644);
378
        if(pidfd < 0) {
379
                JANUS_LOG(LOG_FATAL, "Error opening/creating PID file %s, does Janus have enough permissions?\n", pidfile);
380
                return -1;
381
        }
382
        pidf = fdopen(pidfd, "r+");
383
        if(pidf == NULL) {
384
                JANUS_LOG(LOG_FATAL, "Error opening/creating PID file %s, does Janus have enough permissions?\n", pidfile);
385
                close(pidfd);
386
                return -1;
387
        }
388
        /* Try locking the PID file */
389
        int pid = 0;
390
        if(flock(pidfd, LOCK_EX|LOCK_NB) < 0) {
391 c07b32bd Lorenzo Miniero
                if(fscanf(pidf, "%d", &pid) == 1) {
392
                        JANUS_LOG(LOG_FATAL, "Error locking PID file (lock held by PID %d?)\n", pid);
393
                } else {
394
                        JANUS_LOG(LOG_FATAL, "Error locking PID file (lock held by unknown PID?)\n");
395
                }
396 4ea57952 Lorenzo Miniero
                fclose(pidf);
397
                return -1;
398
        }
399
        /* Write the PID */
400
        pid = getpid();
401
        if(fprintf(pidf, "%d\n", pid) < 0) {
402
                JANUS_LOG(LOG_FATAL, "Error writing PID in file, error %d (%s)\n", errno, strerror(errno));
403
                fclose(pidf);
404
                return -1;
405
        }
406
        fflush(pidf);
407
        /* We're done */
408
        return 0;
409
}
410
411
int janus_pidfile_remove(void) {
412
        if(pidfile == NULL || pidfd < 0 || pidf == NULL)
413
                return 0;
414
        /* Unlock the PID file and remove it */
415
        if(flock(pidfd, LOCK_UN) < 0) {
416
                JANUS_LOG(LOG_FATAL, "Error unlocking PID file\n");
417
                fclose(pidf);
418
                close(pidfd);
419
                return -1;
420
        }
421
        fclose(pidf);
422
        unlink(pidfile);
423
        g_free(pidfile);
424
        return 0;
425
}
426 887df302 Andreas Girgensohn
427
void janus_get_json_type_name(int jtype, unsigned int flags, char *type_name) {
428
        /* Longest possible combination is "a non-empty boolean" plus one for null char */
429
        gsize req_size = 20;
430
        /* Don't allow for both "positive" and "non-empty" because that needlessly increases the size. */
431
        if((flags & JANUS_JSON_PARAM_POSITIVE) != 0) {
432
                g_strlcpy(type_name, "a positive ", req_size);
433
        }
434
        else if((flags & JANUS_JSON_PARAM_NONEMPTY) != 0) {
435
                g_strlcpy(type_name, "a non-empty ", req_size);
436
        }
437
        else if(jtype == JSON_INTEGER || jtype == JSON_ARRAY || jtype == JSON_OBJECT) {
438
                g_strlcpy(type_name, "an ", req_size);
439
        }
440
        else {
441
                g_strlcpy(type_name, "a ", req_size);
442
        }
443
        switch(jtype) {
444
                case JSON_TRUE:
445
                        g_strlcat(type_name, "boolean", req_size);
446
                        break;
447
                case JSON_INTEGER:
448
                        g_strlcat(type_name, "integer", req_size);
449
                        break;
450
                case JSON_REAL:
451
                        g_strlcat(type_name, "real", req_size);
452
                        break;
453
                case JSON_STRING:
454
                        g_strlcat(type_name, "string", req_size);
455
                        break;
456
                case JSON_ARRAY:
457
                        g_strlcat(type_name, "array", req_size);
458
                        break;
459
                case JSON_OBJECT:
460
                        g_strlcat(type_name, "object", req_size);
461
                        break;
462
                default:
463
                        break;
464
        }
465
}
466
467
gboolean janus_json_is_valid(json_t *val, json_type jtype, unsigned int flags) {
468
        gboolean is_valid = (json_typeof(val) == jtype || (jtype == JSON_TRUE && json_typeof(val) == JSON_FALSE));
469
        if(!is_valid)
470
                return FALSE;
471
        if((flags & JANUS_JSON_PARAM_POSITIVE) != 0) {
472
                switch(jtype) {
473
                        case JSON_INTEGER:
474
                                is_valid = (json_integer_value(val) >= 0);
475
                                break;
476
                        case JSON_REAL:
477
                                is_valid = (json_real_value(val) >= 0);
478
                                break;
479
                        default:
480
                                break;
481
                }
482
        }
483
        else if((flags & JANUS_JSON_PARAM_NONEMPTY) != 0) {
484
                switch(jtype) {
485
                        case JSON_STRING:
486 8ef4486f Pierce Lopez
                                is_valid = (strlen(json_string_value(val)) > 0);
487 887df302 Andreas Girgensohn
                                break;
488
                        case JSON_ARRAY:
489
                                is_valid = (json_array_size(val) > 0);
490
                                break;
491
                        default:
492
                                break;
493
                }
494
        }
495
        return is_valid;
496
}