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