00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "hdhomerun.h"
00042
00043 #if !defined(HDHOMERUN_DEBUG_HOST)
00044 #define HDHOMERUN_DEBUG_HOST "debug.silicondust.com"
00045 #endif
00046 #if !defined(HDHOMERUN_DEBUG_PORT)
00047 #define HDHOMERUN_DEBUG_PORT 8002
00048 #endif
00049
00050 #define HDHOMERUN_DEBUG_CONNECT_RETRY_TIME 30000
00051 #define HDHOMERUN_DEBUG_CONNECT_TIMEOUT 10000
00052 #define HDHOMERUN_DEBUG_SEND_TIMEOUT 10000
00053
00054 struct hdhomerun_debug_message_t
00055 {
00056 struct hdhomerun_debug_message_t *next;
00057 struct hdhomerun_debug_message_t *prev;
00058 char buffer[2048];
00059 };
00060
00061 struct hdhomerun_debug_t
00062 {
00063 pthread_t thread;
00064 volatile bool_t enabled;
00065 volatile bool_t terminate;
00066 char *prefix;
00067
00068 pthread_mutex_t print_lock;
00069 pthread_mutex_t queue_lock;
00070 pthread_mutex_t send_lock;
00071
00072 struct hdhomerun_debug_message_t *queue_head;
00073 struct hdhomerun_debug_message_t *queue_tail;
00074 uint32_t queue_depth;
00075
00076 uint64_t connect_delay;
00077
00078 char *file_name;
00079 FILE *file_fp;
00080 hdhomerun_sock_t sock;
00081 };
00082
00083 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg);
00084
00085 struct hdhomerun_debug_t *hdhomerun_debug_create(void)
00086 {
00087 struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)calloc(1, sizeof(struct hdhomerun_debug_t));
00088 if (!dbg) {
00089 return NULL;
00090 }
00091
00092 dbg->sock = HDHOMERUN_SOCK_INVALID;
00093
00094 pthread_mutex_init(&dbg->print_lock, NULL);
00095 pthread_mutex_init(&dbg->queue_lock, NULL);
00096 pthread_mutex_init(&dbg->send_lock, NULL);
00097
00098 if (pthread_create(&dbg->thread, NULL, &hdhomerun_debug_thread_execute, dbg) != 0) {
00099 free(dbg);
00100 return NULL;
00101 }
00102
00103 return dbg;
00104 }
00105
00106 void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg)
00107 {
00108 if (!dbg) {
00109 return;
00110 }
00111
00112 dbg->terminate = TRUE;
00113 pthread_join(dbg->thread, NULL);
00114
00115 if (dbg->prefix) {
00116 free(dbg->prefix);
00117 }
00118 if (dbg->file_name) {
00119 free(dbg->file_name);
00120 }
00121 if (dbg->file_fp) {
00122 fclose(dbg->file_fp);
00123 }
00124 if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
00125 hdhomerun_sock_destroy(dbg->sock);
00126 }
00127
00128 free(dbg);
00129 }
00130
00131
00132 static void hdhomerun_debug_close_internal(struct hdhomerun_debug_t *dbg)
00133 {
00134 if (dbg->file_fp) {
00135 fclose(dbg->file_fp);
00136 dbg->file_fp = NULL;
00137 }
00138
00139 if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
00140 hdhomerun_sock_destroy(dbg->sock);
00141 dbg->sock = HDHOMERUN_SOCK_INVALID;
00142 }
00143 }
00144
00145 void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout)
00146 {
00147 if (!dbg) {
00148 return;
00149 }
00150
00151 if (timeout > 0) {
00152 hdhomerun_debug_flush(dbg, timeout);
00153 }
00154
00155 pthread_mutex_lock(&dbg->send_lock);
00156 hdhomerun_debug_close_internal(dbg);
00157 dbg->connect_delay = 0;
00158 pthread_mutex_unlock(&dbg->send_lock);
00159 }
00160
00161 void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename)
00162 {
00163 if (!dbg) {
00164 return;
00165 }
00166
00167 pthread_mutex_lock(&dbg->send_lock);
00168
00169 if (!filename && !dbg->file_name) {
00170 pthread_mutex_unlock(&dbg->send_lock);
00171 return;
00172 }
00173 if (filename && dbg->file_name) {
00174 if (strcmp(filename, dbg->file_name) == 0) {
00175 pthread_mutex_unlock(&dbg->send_lock);
00176 return;
00177 }
00178 }
00179
00180 hdhomerun_debug_close_internal(dbg);
00181 dbg->connect_delay = 0;
00182
00183 if (dbg->file_name) {
00184 free(dbg->file_name);
00185 dbg->file_name = NULL;
00186 }
00187 if (filename) {
00188 dbg->file_name = strdup(filename);
00189 }
00190
00191 pthread_mutex_unlock(&dbg->send_lock);
00192 }
00193
00194 void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix)
00195 {
00196 if (!dbg) {
00197 return;
00198 }
00199
00200 pthread_mutex_lock(&dbg->print_lock);
00201
00202 if (dbg->prefix) {
00203 free(dbg->prefix);
00204 dbg->prefix = NULL;
00205 }
00206
00207 if (prefix) {
00208 dbg->prefix = strdup(prefix);
00209 }
00210
00211 pthread_mutex_unlock(&dbg->print_lock);
00212 }
00213
00214 void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg)
00215 {
00216 if (!dbg) {
00217 return;
00218 }
00219
00220 dbg->enabled = TRUE;
00221 }
00222
00223 void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg)
00224 {
00225 if (!dbg) {
00226 return;
00227 }
00228
00229 dbg->enabled = FALSE;
00230 }
00231
00232 bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg)
00233 {
00234 if (!dbg) {
00235 return FALSE;
00236 }
00237
00238 return dbg->enabled;
00239 }
00240
00241 void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout)
00242 {
00243 if (!dbg) {
00244 return;
00245 }
00246
00247 timeout = getcurrenttime() + timeout;
00248
00249 while (getcurrenttime() < timeout) {
00250 pthread_mutex_lock(&dbg->queue_lock);
00251 struct hdhomerun_debug_message_t *message = dbg->queue_tail;
00252 pthread_mutex_unlock(&dbg->queue_lock);
00253
00254 if (!message) {
00255 return;
00256 }
00257
00258 msleep_approx(10);
00259 }
00260 }
00261
00262 void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...)
00263 {
00264 va_list args;
00265 va_start(args, fmt);
00266 hdhomerun_debug_vprintf(dbg, fmt, args);
00267 va_end(args);
00268 }
00269
00270 void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args)
00271 {
00272 if (!dbg) {
00273 return;
00274 }
00275 if (!dbg->enabled) {
00276 return;
00277 }
00278
00279 struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t));
00280 if (!message) {
00281 return;
00282 }
00283
00284 char *ptr = message->buffer;
00285 char *end = message->buffer + sizeof(message->buffer) - 2;
00286 *end = 0;
00287
00288
00289
00290
00291 time_t current_time = time(NULL);
00292 ptr += strftime(ptr, end - ptr, "%Y%m%d-%H:%M:%S ", localtime(¤t_time));
00293 if (ptr > end) {
00294 ptr = end;
00295 }
00296
00297
00298
00299
00300 pthread_mutex_lock(&dbg->print_lock);
00301
00302 if (dbg->prefix) {
00303 int len = snprintf(ptr, end - ptr, "%s ", dbg->prefix);
00304 len = (len <= 0) ? 0 : len;
00305 ptr += len;
00306 if (ptr > end) {
00307 ptr = end;
00308 }
00309 }
00310
00311 pthread_mutex_unlock(&dbg->print_lock);
00312
00313
00314
00315
00316 int len = vsnprintf(ptr, end - ptr, fmt, args);
00317 len = (len < 0) ? 0 : len;
00318 ptr += len;
00319 if (ptr > end) {
00320 ptr = end;
00321 }
00322
00323
00324
00325
00326 if ((ptr[-1] != '\n') && (ptr + 1 <= end)) {
00327 *ptr++ = '\n';
00328 }
00329
00330
00331
00332
00333 if (ptr + 1 > end) {
00334 ptr = end - 1;
00335 }
00336 *ptr++ = 0;
00337
00338
00339
00340
00341 pthread_mutex_lock(&dbg->queue_lock);
00342
00343 message->prev = NULL;
00344 message->next = dbg->queue_head;
00345 dbg->queue_head = message;
00346 if (message->next) {
00347 message->next->prev = message;
00348 } else {
00349 dbg->queue_tail = message;
00350 }
00351 dbg->queue_depth++;
00352
00353 pthread_mutex_unlock(&dbg->queue_lock);
00354 }
00355
00356
00357 static bool_t hdhomerun_debug_output_message_file(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
00358 {
00359 if (!dbg->file_fp) {
00360 uint64_t current_time = getcurrenttime();
00361 if (current_time < dbg->connect_delay) {
00362 return FALSE;
00363 }
00364 dbg->connect_delay = current_time + 30*1000;
00365
00366 dbg->file_fp = fopen(dbg->file_name, "a");
00367 if (!dbg->file_fp) {
00368 return FALSE;
00369 }
00370 }
00371
00372 fprintf(dbg->file_fp, "%s", message->buffer);
00373 fflush(dbg->file_fp);
00374
00375 return TRUE;
00376 }
00377
00378
00379 static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
00380 {
00381 if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
00382 uint64_t current_time = getcurrenttime();
00383 if (current_time < dbg->connect_delay) {
00384 return FALSE;
00385 }
00386 dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME;
00387
00388 dbg->sock = hdhomerun_sock_create_tcp();
00389 if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
00390 return FALSE;
00391 }
00392
00393 uint32_t remote_addr = hdhomerun_sock_getaddrinfo_addr(dbg->sock, HDHOMERUN_DEBUG_HOST);
00394 if (remote_addr == 0) {
00395 hdhomerun_debug_close_internal(dbg);
00396 return FALSE;
00397 }
00398
00399 if (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) {
00400 hdhomerun_debug_close_internal(dbg);
00401 return FALSE;
00402 }
00403 }
00404
00405 size_t length = strlen(message->buffer);
00406 if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) {
00407 hdhomerun_debug_close_internal(dbg);
00408 return FALSE;
00409 }
00410
00411 return TRUE;
00412 }
00413
00414 static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
00415 {
00416 pthread_mutex_lock(&dbg->send_lock);
00417
00418 bool_t ret;
00419 if (dbg->file_name) {
00420 ret = hdhomerun_debug_output_message_file(dbg, message);
00421 } else {
00422 ret = hdhomerun_debug_output_message_sock(dbg, message);
00423 }
00424
00425 pthread_mutex_unlock(&dbg->send_lock);
00426 return ret;
00427 }
00428
00429 static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg)
00430 {
00431 pthread_mutex_lock(&dbg->queue_lock);
00432
00433 struct hdhomerun_debug_message_t *message = dbg->queue_tail;
00434 dbg->queue_tail = message->prev;
00435 if (message->prev) {
00436 message->prev->next = NULL;
00437 } else {
00438 dbg->queue_head = NULL;
00439 }
00440 dbg->queue_depth--;
00441
00442 pthread_mutex_unlock(&dbg->queue_lock);
00443
00444 free(message);
00445 }
00446
00447 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
00448 {
00449 struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg;
00450
00451 while (!dbg->terminate) {
00452
00453 pthread_mutex_lock(&dbg->queue_lock);
00454 struct hdhomerun_debug_message_t *message = dbg->queue_tail;
00455 uint32_t queue_depth = dbg->queue_depth;
00456 pthread_mutex_unlock(&dbg->queue_lock);
00457
00458 if (!message) {
00459 msleep_approx(250);
00460 continue;
00461 }
00462
00463 if (queue_depth > 1024) {
00464 hdhomerun_debug_pop_and_free_message(dbg);
00465 continue;
00466 }
00467
00468 if (!hdhomerun_debug_output_message(dbg, message)) {
00469 msleep_approx(250);
00470 continue;
00471 }
00472
00473 hdhomerun_debug_pop_and_free_message(dbg);
00474 }
00475
00476 return 0;
00477 }